Whilst base R plots are quick and useful for examining our data, they don’t always offer the flexibility and attractive customization options that we’d like for a presentation or manuscript. This is where ggplot2 comes in.

This session will teach you the basics of using ggplot2 to visualize data in R. ggplot was developed in 2005 by Hadley Wickham as an open source data visualization package for R. With ggplot2, you can create plots that range from simple scatter diagrams to complex custom plots that are (almost) completely customizable.

Resources:

https://towardsdatascience.com/guide-to-data-visualization-with-ggplot2-in-a-hour-634c7e3bc9dd

https://ggplot2.tidyverse.org/reference/

https://www.youtube.com/@Riffomonas


Install necessary packages

ggplot2 is included in the tidyverse package, so we can simply install and load tidyverse for everything we’ll cover in this session.

if (!requireNamespace(c("tidyverse","ggtext"), quietly = TRUE))
  install.packages(c("tidyverse","ggtext"))

library(tidyverse)
library(ggtext)

Understanding ggplot syntax

There are three fundamental elements that go into constructing a plot with ggplot2:

Data - dataframe to be plotted data = dataframe

Aesthetics - maps variables to elements of the plot (i.e. x axis, y axis, color scheme, etc.) mapping = aes()

Geometry/Layers - visual elements used for the data + geom_function()

The typical input code for ggplot will usually look something like this:

ggplot(data = df, # input data
       mapping = aes(x = var1, # input mapping aesthetics
                     y = var2,
                     color = var3)) +
  geom_point() # add plotting layer

Setting up data for success

One of the most important parts of getting ready to plot data in R is ensuring that your data are “tidy”. When passing instructions to ggplot2, the program interprets dataframes in a fixed way:

columns are variables

rows are observations

Let’s examine a dataframe to better understand how ggplot2 interprets data. Here, we will be using the Iris dataset. The Iris dataset is built-in to R and was introduced by British statistician and biologist Ronald A. Fisher in 1936. Fisher collected the data to study the variation in iris flowers of three different species: Iris setosa, Iris versicolor, and Iris virginica.

head(iris) # view the first six rows of the dataframe

Looking at the first rows of this dataframe we can see that each variable is contained in a column and each row is an observation. This means that if you have replicate measurements (as in this dataset, there are multiple measurements of each variable per species) you will need to have a row per replicate rather than storing the replicate data in columns.

Other important notes about this dataset:

Let’s build a plot!

ggplot2 builds plots in layers. You can start with a layer showing raw data, then continue to add up additional elements to produce your desired graph. This approach will help you reduce the gap between the expected outcomes in your head and the plots in reality.

ggplot(data = iris, # use 'data' argument to tell ggplot which dataframe we want to plot from
       mapping = aes(x = Petal.Length, # mapping determines which variables are assigned to plot elements
                     y = Petal.Width)) -> basicPlot

basicPlot

We have told ggplot2 which variables we want to plot on the x and y axes but we have not told ggplot2 which geometric elements (i.e. geoms) to use to construct the plot, so all we have are the axes and a blank plot.

What are geoms?

Geoms are the geometric objects (e.g. lines, bars, etc.) that determine how observations are rendered. Layering elements in a plot usually starts with adding geoms. Let’s add geom_point() to our basic plot to create a scatter plot:

ggplot(data = iris,
       aes(x = Petal.Length, # it is very common to see 'mapping =' omitted from the code - ggplot will accept either
           y = Petal.Width)) + # use a + to add elements to your plot
  geom_point() -> basicScatter

basicScatter

Another way you can add layers to a plot is by simply adding them to the end of the object that we assigned our first plot to. It is very common to see this in online guides and forums (such as Stack Overflow) where you might look for help with R coding:

basicPlot +
  geom_point()

Although this generates the same output, I would generally avoid making your plots this way - if you end up with something that isn’t quite working as expected I find it can be easier to fix if all your code is laid out in front of you, rather than having to revisit each individual step in the process of making your plot.

Now we’ll start to add some more elements to our mapping aesthetics to better illustrate our data.


IMPORTANT NOTE

The initial mapping that you specify in the ggplot2 command (i.e. axes, color, size, etc.) are by default used globally for the plot and are carried over to any geoms you add in the following code. Each geom can have it’s own separate mapping aesthetics, which can allow you to create more complex plots. If you ever run into issues where a geom is not behaving as you would expect, take a look back through your code and check where your aesthetics were assigned, and how they apply to the geoms you are trying to layer.


Let’s color our points by Species:

ggplot(data=iris, 
       mapping = aes(x=Petal.Length, 
                     y=Petal.Width, 
                     color = Species)) + # tell ggplot that color is determined by Species variable
  geom_point() -> colorScatter
          
colorScatter

Now we can add a simple regression using geom_smooth() and we can demonstrate how changing global vs. specific aesthetics affect geoms. Some geoms have specialized arguments that allow them to function. In this case, geom_smooth() allows us to tell it which method to use to generate the curve that it will plot. We will opt for lm which is a linear model.

ggplot(data=iris, 
       mapping = aes(x=Petal.Length, 
                     y=Petal.Width, 
                     color = Species)) + 
  geom_point() +
  geom_smooth(method = lm) -> colorScatterlm
          
colorScatterlm

This creates three separate curves that map to the points from each Species by color, as this is what is specified in the global aesthetics. Let’s change that and plot a curve that spans all points:

ggplot(data=iris, 
       mapping = aes(x=Petal.Length, 
                     y=Petal.Width)) + # remove color from global aesthetics
  geom_point(aes(color = Species)) + # set geom_point aesthetics - this will only color points
  geom_smooth(method = lm) -> colorScatterlm2

colorScatterlm2

Now we can see that the points are still colored by Species, but the regression is not.

Let’s play with some more aesthetics:

ggplot(data=iris, 
       mapping = aes(x=Petal.Length, 
                     y=Petal.Width)) + 
  geom_point(aes(color = "blue")) + 
  geom_smooth(method = lm) -> basicPlotBlue

basicPlotBlue

Notice how even though we have changed the aesthetic of the points to be “blue”, it has not made them blue. If we want to make all the points one color (or a different shape, or a different size) these are not set by aesthetics, as they are not dependent on a variable.

Let’s make our points blue and change their shape:

ggplot(data = iris,
       mapping = aes(x = Petal.Length,
                     y = Petal.Width)) +
  geom_point(color = "blue", # note that these options are not parsed through the aes() argument
             size = 5,
             shape = 1) -> openCircleBlue

openCircleBlue


# shapes are defined by a numerical value
# available shapes can be viewed at https://www.datanovia.com/en/blog/ggplot-point-shapes-best-tips/ or by using ggpubr::show_point_shapes()

There are other ways we can control how each layer is rendered. Let’s start with controlling “scales”. Scales allow us to edit specific elements of the aesthetics and are named in a uniform manner than describes how they act and what they affect. The names are made up of three pieces separated by “_“:

Within the scales there are several options we can edit:

Let’s use scale_color_manual to manually select some colors for our plot.

(There are set named colors that can be used in R https://stat.columbia.edu/~tzheng/files/Rcolor.pdf but you can also use hex codes. Make sure to use color blind friendly color schemes for figures you plan to present or publish!)

ggplot(data = iris,
       mapping = aes(x = Petal.Length,
                     y = Petal.Width,
                     color = Species)) +
  geom_point() +
  geom_smooth(method = lm) +
  scale_color_manual(name = "Iris species",
                     values = c("setosa" = "pink",
                                "versicolor" = "plum",
                                "virginica"="seagreen3"),
                     labels = c("Iris setosa",
                                "Iris versicolor",
                                "Iris virginica")) -> multiColor

multiColor

Now we have some different colors and data labels in our plot of the Iris data. The name and labels options in the scale are useful for changing how the data are labeled in your plot without needing to manipulate the raw data.

We can also use continuous color scales to visually represent changes in values:

ggplot(data = iris,
       mapping = aes(x = Petal.Length,
                     y = Petal.Width)) +
  geom_point(aes(color = Petal.Length)) +
  geom_smooth(method = lm) -> blueCont

blueCont

You can also use other variables within the dataframe to control the aesthetics of the plot.

ggplot(data = iris,
       mapping = aes(x = Petal.Length,
                     y = Petal.Width)) +
  geom_point(aes(color = Sepal.Length, # color is dependent on sepal length
                 size = Sepal.Width)) + # point size is dependent on sepal width
  geom_smooth(method = lm,
              color = "black",
              se = FALSE) + # change the color of the curve
  scale_color_gradient(high = "purple",
                       low = "orange") # manually set the colors of the gradient scale

Obviously there is a little too much data now contained in this plot for it to be particularly useful, but it is a good example of how much data you can display and the different ways you can present it using R.

Let’s tidy up our plot and make something that looks a little more “publication-ready”:

First, let’s assign our chosen color scale to a vector object so we can call the same colors for any future plots without needing to write out the code every time. This time I’m going to use a color blind friendly palette generated using this tool: https://davidmathlogic.com/colorblind.

plotColors <- c("setosa" = "#648FFF",
                "versicolor" = "#DC267F",
                "virginica"="#FFB000")
ggplot(data = iris,
       mapping = aes(x = Petal.Length,
                     y = Petal.Width)) +
  geom_point(aes(color = Species)) +
  geom_smooth(method = lm,
              color = "black") +
  theme_bw() + # this is a built-in theme that removes the gray plot background
  scale_color_manual(values = plotColors) + # direct ggplot to our color vector
  ylab("Petal width (mm)") + # change y axis label - can also be done with scales
  labs(x = "Petal length (mm)",
  color = "Iris species") + # change legend title - can also be done with scales as previously
  ggtitle("Petal width by petal length per species") + # add plot title 
  theme(plot.title = element_text(hjust = 0.5)) -> multiColorTidy # center plot title

multiColorTidy

Perfect! Now we can write our plot to a pdf:

pdf("multi.color.iris.plot.lm.pdf", # file name to write to
    height = 4, # plot height in inches
    width = 6) # plot width in inches

multiColorTidyLm # tell R which plot to write to file

dev.off() # this tells R that you're done creating a file

Or we can use ggsave(), which is a function of ggplot2 to save as any other graphics file type:

ggsave(plot = multiColorTidyLm, # specify plot
       "multi.color.iris.plot.lm.tiff", # specify file name
       height = 4, # plot height
       width = 6, # plot width
       units = c("in"), # specify which units to use for height and width
       device = "tiff") # specify file type for saving - ggsave will also guess depending on the extension used in file name 

Plotting group means and error bars

Another way that we may want to plot our data is by plotting both group means and individual data points. This can help people better visualize the spread of our data. This is easy enough with geoms like geom_boxplot() and geom_violin() that have group metrics built into their functionality.

ggplot(data = iris,
       aes(x = Species,
           y = Petal.Length,
           fill = Species)) + 
  geom_boxplot(outliers = FALSE) +
  geom_point()

You can see that geom_boxplot has automatically generated a box that displays the median (thick line) and box that spans the 25th - 75th percentiles, with whiskers that extend to the furthest value no more than 1.5 X the IQR from the box. Values beyond the whiskers would be counted as outliers and plotted separately. This is great for taking a quick summary look at your data.

But what if we want to use something like geom_bar() that does not have built in group functionality?

There are a couple of ways we could solve this issue using functions in the tidyverse package. First, we could create a summary table that contains grouped information for our data using summarise.

iris %>%
  group_by(Species) %>% # group_by tells R which variable to use to group observations
  summarise(mean.Petal.Length = mean(Petal.Length), # add a column containing mean values per species
            standard.deviation = sd(Petal.Length)) -> irisSummary # add a column containing standard deviation

head(irisSummary)

We can use the summarise function to create a new dataframe that contains a mean and standard deviation for each species. We can write this to a new object and then use this for plotting by providing each geom with a different dataframe.

ggplot() + # we do not want global mapping or data for this plot so none is put in the ggplot call
  geom_col(data = irisSummary, # set the dataframe for the columns
           aes(x = Species,
               y = mean.Petal.Length,
               fill = Species),
           alpha = 0.5) +
  geom_errorbar(data = irisSummary, # set the dataframe for the error bars
                aes(x = Species,
                    ymin = (mean.Petal.Length - standard.deviation), # set the minimum error bar value
                    ymax = (mean.Petal.Length + standard.deviation)), # set the maximum error bar value
                width = 0.2) +
  geom_jitter(data = iris, # set the dataframe for the points
              aes(x = Species,
                  y = Petal.Length,
                  color = Species),
              width = 0.2, # make the total spread of the points narrower
              shape = 1) # set the shape to open circle

Now we can see both the mean and individual values on our bar plot.

Another, more streamlined, way of doing this is using stat_summary, where we remove the need to create a separate dataframe by using functions within the ggplot package.

ggplot(data = iris,
       aes(x = Species,
           y = Petal.Length)) +
  stat_summary(geom = "col", # identify which geom we want
               fun.data = mean_se, # tell stat_summary which function to apply to summarise the data
               aes(fill = Species), # set aesthetics as normal
               alpha = 0.5) +
  stat_summary(geom = "errorbar",
               fun.data = mean_se,
               color = "black",
               width = 0.2) +
  geom_jitter(aes(color = Species),
              shape = 1,
              width = 0.2)

Voilà! We have almost same plot as above but with a step removed. However, you may have noticed that we used function mean_se, which calculates the mean and standard error for a vector of y values at each unique x value (i.e. the function receives a vector of values for Petal.Length for each Species) and most of the time we like to use standard deviation. stat_summary does not offer this function as part of the package - so what do we do? Create our own.

mean.sd <- function(x){
  tibble(y = mean(x), # tell the function that we want a tibble output (similar to dataframe)
         ymin = y - sd(x), # calculates the minimum value for error bar
         ymax = y + sd(x)) # calculates the maximum value for error bar
}

Now we can create our plot:

ggplot(data = iris,
       aes(x = Species,
           y = Petal.Length)) +
  stat_summary(geom = "col", 
               fun.data = mean.sd, 
               aes(fill = Species),
               alpha = 0.5) +
  stat_summary(geom = "errorbar",
               fun.data = mean.sd,
               color = "black",
               width = 0.2) +
  geom_jitter(aes(color = Species),
              shape = 1,
              width = 0.2)

Facets

Faceting is a technique that allows us to separate data out into panels based on a variable in the dataframe. This is useful for visualizing complex data where it may be easier to see patterns when the data are separated.

There are two methods to create facets in a plot: facet_wrap() and facet_grid(). If you are only creating facets based on one variable (e.g. species) you can use facet_wrap() but if you have a more complex plot where you want to create facets based on two variables (e.g. species and time point) you need to use facet_grid().

Let’s pull up another of R’s built-in datasets (mtcars) that will allow us to see both of these in action. mtcars is built from data extracted from the 1974 Motor Trend US magazine, and comprises fuel consumption and 10 aspects of automobile design and performance for 32 automobiles (1973–74 models).

head(mtcars)

Let’s look at mpg (Miles per US Gallon) plotted against hp (Gross horsepower).

ggplot(data = mtcars,
       aes(x = hp,
           y = mpg,
           color = mpg)) +
  geom_point(size = 3)

Now let’s use facet_wrap() to split these data up by vs (Enginge shape, 0 = V, 1 = straight).

ggplot(data = mtcars,
       aes(x = hp,
           y = mpg,
           color = mpg)) +
  geom_point(size = 3) +
  facet_wrap(~ vs)

Let’s add another variable facet with facet_grid() and split the data by am (Transmission, 0 = automatic, 1 = manual) as well.

ggplot(data = mtcars,
       aes(x = hp,
           y = mpg,
           color = mpg)) +
  geom_point(size = 3) +
  facet_grid(cols = vars(vs), # assign a variable to the column panels
             rows = vars(am)) # assign a variable to the row panels

We can see that there are different correlations between hp and mpg depending on the other qualities of the car. However, this plot is now difficult to read because both variables are binaries, meaning it’s hard to tell what’s what. Let’s tidy up these plots and add some labels.

Changing the panel labels without changing the underlying data is slightly more complex than changing axis titles, so let’s look at how to do that.

vsLabs <- c("0" = "V-shaped",
            "1" = "Straight") # create a vector that matches the binary variables to their values

amLabs <- c("0" = "Automatic",
            "1" = "Manual") # do the same for the am variable

ggplot(data = mtcars,
       aes(x = hp,
           y = mpg,
           color = mpg)) +
  geom_point(size = 3) +
  facet_grid(cols = vars(vs),
             rows = vars(am),
             labeller = labeller(.cols = vsLabs, # use the labeller function to assign these labels to the rows and columns of the plot
                                 .rows = amLabs)) -> facetPlot

facetPlot

Let’s tidy the rest of this plot up and then save it to file.

facetPlot +
  scale_color_gradient(name = "Miles per\nUS Gallon", # \n starts a new line in the legend title
                       high = "purple",
                       low = "orange") + # change color of scale
  theme_bw() +
  xlab("Gross horsepower") + # add x axis title
  ylab("Miles per US Gallon") + # add y axis title
  theme(strip.background = element_rect(fill = "white")) -> facetPlotTidy # remove grey background from panel titles

facetPlotTidy

Now we can use the same methods as earlier to save our plot to either a PDF or image file (or both!).

pdf("multi.facet.mtcars.plot.pdf", # file name to write to
    height = 4, # plot height in inches
    width = 6) # plot width in inches

facetPlotTidy # tell R which plot to write to file

dev.off() # this tells R that you're done creating a file
ggsave(plot = facetPlotTidy, # specify plot
       "multi.facet.mtcars.plot.tiff", # specify file name
       height = 4, # plot height
       width = 6, # plot width
       units = c("in"), # specify which units to use for height and width
       device = "tiff") # specify file type for saving - ggsave will also guess depending on the extension used in file name

Plotting a time course

For many experiments, it’s important to be able to plot a time course. Let’s load in some example colony count data from an experiment growing four species of bacteria in both high and low iron conditions, with time points at 0, 6, and 24 hours.

read.csv("cfu_counts_raw.csv") -> counts # read in counts

Let’s take a quick look at the format of the data we just loaded and check that the format looks correct for plotting.

head(counts)

Our dataframe has columns as variables and rows as observations so we’re good to go!

In order to plot a time course as a discrete variable that runs along the x axis, we need to change the time variable from numeric to a factor in both the raw counts and group means dataframes. Factors can help us control the order in which observations are plotted. By default, ggplot will plot numeric variables in ascending order and character or factor variables in alphabetical order. So, we’ll also set the iron level as a factor because I want to plot the low iron condition before the high iron condition.

counts$time <- factor(counts$time)

counts$iron <- factor(counts$iron,
                      levels = c("Low iron","High iron"))

Now we can set our custom colors for the plot.

speciesCols <- c("Pseudomonas aeruginosa" = "#43ba8f",
            "Staphylococcus aureus" = "#fec44f",
            "Streptococcus sanguinis" = "#4292c6",
            "Burkholderia orbicola" = "#d57bd4")

Let’s create a line plot of log10 CFU/mL per species over time, with facets showing the high and low iron. We will plot a ribbon that represents the standard deviation (sd), thin lines that represent each replicate (tech.rep), and a thick line representing the mean CFU/mL for each species (mean.cfu). We’ll utilize the stat_summary function that we saw earlier.

counts %>%
  mutate(log10.cfu = log10(cfu)) %>% # create a new column with log10 CFU values
  ggplot(aes(x = time,
             y = log10.cfu)) +
  stat_summary(geom = "ribbon",
               fun.data = mean.sd,
               aes(group = species, # group aesthetic specifies how the lines are joined together
                   fill = species),
               alpha = 0.5) +
  geom_line(aes(group = interaction(species,iron,tech.rep),
                color = species),
            linewidth = 0.1,
            alpha = 0.7) +
  stat_summary(geom = "line",
               fun.data = mean.sd,
               aes(color = species,
                   group = interaction(species,iron)),
               linewidth = 1) +
  scale_color_manual(name = "Species", # both color and fill must have the same name if we want to combine the legend
                     values = speciesCols) +
  scale_fill_manual(name = "Species",
                    values = speciesCols) +
  labs(x = "Time (h)", # set axis labels
       y = "Log<sub>10</sub> CFU/mL") +
  theme_bw() + # remove grey background
  facet_grid(cols = vars(iron)) + # facet plot by high/low iron
  theme(strip.background = element_rect(fill = "white", # remove grey background from facet titles
                                        color = "black"),
        legend.text = element_text(face = "italic"), # set legend font to italic
        legend.position = "inside", # move legend inside bounds of plot
        legend.position.inside = c(0.8,0.2), # use a vector to set x and y position (0 - 1)
        legend.background = element_rect(fill = "white", # set box around legend
                                         color = "black"),
        ,
        axis.title.y = element_markdown()) # allow axis title to read html code for subscript

Activities

Green 1

Create a scatter plot using the columns Sepal.Length (x) and Sepal.Width (y) from the iris dataset.

ggplot(data = iris, 
       aes(x = Sepal.Length, 
           y = Sepal.Width)) + 
  geom_point(aes(color = Species)) +
  geom_smooth(method = lm)

Green 2

Make a plot where all the points are green, and the line is colored by the species of iris.

ggplot(data = iris,
       aes(x = Petal.Length,
           y = Petal.Width)) +
  geom_point(color = "green") +
  geom_smooth(aes(color = Species),
              method = lm)

Blue 1

Make a plot that includes regression lines for individual species as well as the overall data.

ggplot(data = iris, 
       aes(x = Petal.Length, 
           y = Petal.Width)) +
  geom_point(aes(color = Species)) +
  geom_smooth(aes(color = Species), 
              method = lm) +
  geom_smooth(method = lm, 
              color="blue")
LS0tCnRpdGxlOiAiSW50cm9kdWN0aW9uIHRvIGdncGxvdCIKYXV0aG9yOiAiWWFzbWluIEhpbGxpYW0sIFBoRCIKZGF0ZTogIjIwMjUtMDctMDUiCm91dHB1dDogaHRtbF9ub3RlYm9vawotLS0KCmBgYHtyIHNldHVwLCBpbmNsdWRlID0gRkFMU0V9CmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSwKICAgICAgICAgICAgICAgICAgICAgIGNvbGxhcHNlID0gVFJVRSkKYGBgCgoKV2hpbHN0IGJhc2UgYFJgIHBsb3RzIGFyZSBxdWljayBhbmQgdXNlZnVsIGZvciBleGFtaW5pbmcgb3VyIGRhdGEsIHRoZXkgZG9uJ3QgYWx3YXlzIG9mZmVyIHRoZSBmbGV4aWJpbGl0eSBhbmQgYXR0cmFjdGl2ZSBjdXN0b21pemF0aW9uIG9wdGlvbnMgdGhhdCB3ZSdkIGxpa2UgZm9yIGEgcHJlc2VudGF0aW9uIG9yIG1hbnVzY3JpcHQuIFRoaXMgaXMgd2hlcmUgYGdncGxvdDJgIGNvbWVzIGluLgoKVGhpcyBzZXNzaW9uIHdpbGwgdGVhY2ggeW91IHRoZSBiYXNpY3Mgb2YgdXNpbmcgYGdncGxvdDJgIHRvIHZpc3VhbGl6ZSBkYXRhIGluIFIuIGdncGxvdCB3YXMgZGV2ZWxvcGVkIGluIDIwMDUgYnkgSGFkbGV5IFdpY2toYW0gYXMgYW4gb3BlbiBzb3VyY2UgZGF0YSB2aXN1YWxpemF0aW9uIHBhY2thZ2UgZm9yIFIuIFdpdGggYGdncGxvdDJgLCB5b3UgY2FuIGNyZWF0ZSBwbG90cyB0aGF0IHJhbmdlIGZyb20gc2ltcGxlIHNjYXR0ZXIgZGlhZ3JhbXMgdG8gY29tcGxleCBjdXN0b20gcGxvdHMgdGhhdCBhcmUgKGFsbW9zdCkgY29tcGxldGVseSBjdXN0b21pemFibGUuCgpSZXNvdXJjZXM6Cgo8aHR0cHM6Ly90b3dhcmRzZGF0YXNjaWVuY2UuY29tL2d1aWRlLXRvLWRhdGEtdmlzdWFsaXphdGlvbi13aXRoLWdncGxvdDItaW4tYS1ob3VyLTYzNGM3ZTNiYzlkZD4KCjxodHRwczovL2dncGxvdDIudGlkeXZlcnNlLm9yZy9yZWZlcmVuY2UvPgoKPGh0dHBzOi8vd3d3LnlvdXR1YmUuY29tL0BSaWZmb21vbmFzPgoKLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgojIyMjIEluc3RhbGwgbmVjZXNzYXJ5IHBhY2thZ2VzCgpgZ2dwbG90MmAgaXMgaW5jbHVkZWQgaW4gdGhlIGB0aWR5dmVyc2VgIHBhY2thZ2UsIHNvIHdlIGNhbiBzaW1wbHkgaW5zdGFsbCBhbmQgbG9hZCBgdGlkeXZlcnNlYCBmb3IgZXZlcnl0aGluZyB3ZSdsbCBjb3ZlciBpbiB0aGlzIHNlc3Npb24uCgpgYGB7ciwgbWVzc2FnZSA9IEZBTFNFfQppZiAoIXJlcXVpcmVOYW1lc3BhY2UoYygidGlkeXZlcnNlIiwiZ2d0ZXh0IiksIHF1aWV0bHkgPSBUUlVFKSkKICBpbnN0YWxsLnBhY2thZ2VzKGMoInRpZHl2ZXJzZSIsImdndGV4dCIpKQoKbGlicmFyeSh0aWR5dmVyc2UpCmxpYnJhcnkoZ2d0ZXh0KQpgYGAKCgojIyMjIFVuZGVyc3RhbmRpbmcgZ2dwbG90IHN5bnRheAoKVGhlcmUgYXJlIHRocmVlIGZ1bmRhbWVudGFsIGVsZW1lbnRzIHRoYXQgZ28gaW50byBjb25zdHJ1Y3RpbmcgYSBwbG90IHdpdGggYGdncGxvdDJgOgoKKipEYXRhKiogLSBkYXRhZnJhbWUgdG8gYmUgcGxvdHRlZCBgZGF0YSA9IGRhdGFmcmFtZWAKCioqQWVzdGhldGljcyoqIC0gbWFwcyB2YXJpYWJsZXMgdG8gZWxlbWVudHMgb2YgdGhlIHBsb3QgKGkuZS4geCBheGlzLCB5IGF4aXMsIGNvbG9yIHNjaGVtZSwgZXRjLikgYG1hcHBpbmcgPSBhZXMoKWAKCioqR2VvbWV0cnkvTGF5ZXJzKiogLSB2aXN1YWwgZWxlbWVudHMgdXNlZCBmb3IgdGhlIGRhdGEgYCsgZ2VvbV9mdW5jdGlvbigpYAoKVGhlIHR5cGljYWwgaW5wdXQgY29kZSBmb3IgZ2dwbG90IHdpbGwgdXN1YWxseSBsb29rIHNvbWV0aGluZyBsaWtlIHRoaXM6CgpgYGB7ciwgZXZhbCA9IEZBTFNFfQpnZ3Bsb3QoZGF0YSA9IGRmLCAjIGlucHV0IGRhdGEKICAgICAgIG1hcHBpbmcgPSBhZXMoeCA9IHZhcjEsICMgaW5wdXQgbWFwcGluZyBhZXN0aGV0aWNzCiAgICAgICAgICAgICAgICAgICAgIHkgPSB2YXIyLAogICAgICAgICAgICAgICAgICAgICBjb2xvciA9IHZhcjMpKSArCiAgZ2VvbV9wb2ludCgpICMgYWRkIHBsb3R0aW5nIGxheWVyCmBgYAoKCiMjIyMgU2V0dGluZyB1cCBkYXRhIGZvciBzdWNjZXNzCgpPbmUgb2YgdGhlIG1vc3QgaW1wb3J0YW50IHBhcnRzIG9mIGdldHRpbmcgcmVhZHkgdG8gcGxvdCBkYXRhIGluIGBSYCBpcyBlbnN1cmluZyB0aGF0IHlvdXIgZGF0YSBhcmUgInRpZHkiLiBXaGVuIHBhc3NpbmcgaW5zdHJ1Y3Rpb25zIHRvIGBnZ3Bsb3QyYCwgdGhlIHByb2dyYW0gaW50ZXJwcmV0cyBkYXRhZnJhbWVzIGluIGEgZml4ZWQgd2F5OgoKKipjb2x1bW5zKiogYXJlIHZhcmlhYmxlcwoKKipyb3dzKiogYXJlIG9ic2VydmF0aW9ucwoKTGV0J3MgZXhhbWluZSBhIGRhdGFmcmFtZSB0byBiZXR0ZXIgdW5kZXJzdGFuZCBob3cgYGdncGxvdDJgIGludGVycHJldHMgZGF0YS4gSGVyZSwgd2Ugd2lsbCBiZSB1c2luZyB0aGUgSXJpcyBkYXRhc2V0LiBUaGUgSXJpcyBkYXRhc2V0IGlzIGJ1aWx0LWluIHRvIGBSYCBhbmQgd2FzIGludHJvZHVjZWQgYnkgQnJpdGlzaCBzdGF0aXN0aWNpYW4gYW5kIGJpb2xvZ2lzdCBSb25hbGQgQS4gRmlzaGVyIGluIDE5MzYuIEZpc2hlciBjb2xsZWN0ZWQgdGhlIGRhdGEgdG8gc3R1ZHkgdGhlIHZhcmlhdGlvbiBpbiBpcmlzIGZsb3dlcnMgb2YgdGhyZWUgZGlmZmVyZW50IHNwZWNpZXM6IElyaXMgc2V0b3NhLCBJcmlzIHZlcnNpY29sb3IsIGFuZCBJcmlzIHZpcmdpbmljYS4KCmBgYHtyLCBtZXNzYWdlID0gRkFMU0V9CmhlYWQoaXJpcykgIyB2aWV3IHRoZSBmaXJzdCBzaXggcm93cyBvZiB0aGUgZGF0YWZyYW1lCmBgYAoKCkxvb2tpbmcgYXQgdGhlIGZpcnN0IHJvd3Mgb2YgdGhpcyBkYXRhZnJhbWUgd2UgY2FuIHNlZSB0aGF0IGVhY2ggKip2YXJpYWJsZSoqIGlzIGNvbnRhaW5lZCBpbiBhIGNvbHVtbiBhbmQgZWFjaCByb3cgaXMgYW4gKipvYnNlcnZhdGlvbioqLiBUaGlzIG1lYW5zIHRoYXQgaWYgeW91IGhhdmUgcmVwbGljYXRlIG1lYXN1cmVtZW50cyAoYXMgaW4gdGhpcyBkYXRhc2V0LCB0aGVyZSBhcmUgbXVsdGlwbGUgbWVhc3VyZW1lbnRzIG9mIGVhY2ggdmFyaWFibGUgcGVyIHNwZWNpZXMpIHlvdSB3aWxsIG5lZWQgdG8gaGF2ZSBhIHJvdyAqcGVyIHJlcGxpY2F0ZSogcmF0aGVyIHRoYW4gc3RvcmluZyB0aGUgcmVwbGljYXRlIGRhdGEgaW4gY29sdW1ucy4KCk90aGVyIGltcG9ydGFudCBub3RlcyBhYm91dCB0aGlzIGRhdGFzZXQ6CgotICAgaXQgY29uc2lzdHMgb2YgZm91ciBudW1lcmljIHZhcmlhYmxlcyAoU2VwYWwuTGVuZ3RoLCBTZXBhbC5XaWR0aCwgUGV0YWwuTGVuZ3RoLCBQZXRhbC5XaWR0aCkgYW5kIG9uZSBjYXRlZ29yaWNhbCB2YXJpYWJsZSAoU3BlY2llcykuIFRoaXMgc3RydWN0dXJlZCBmb3JtYXQgbWFrZXMgaXQgZWFzeSB0byBtYXAgdmFyaWFibGVzIHRvIGFlc3RoZXRpY3MgaW4gYGdncGxvdDJgLgoKLSAgIHRoZSBJcmlzIGRhdGFzZXQgaGFzIGEgYmFsYW5jZWQgY2xhc3MgZGlzdHJpYnV0aW9uLiBFYWNoIG9mIHRoZSB0aHJlZSBzcGVjaWVzIChzZXRvc2EsIHZlcnNpY29sb3IsIHZpcmdpbmljYSkgaGFzIGFuIGVxdWFsIG51bWJlciBvZiBvYnNlcnZhdGlvbnMuIFRoaXMgYmFsYW5jZSBhbGxvd3MgZm9yIGZhaXIgdmlzdWFsIGNvbXBhcmlzb25zIGFuZCBhdm9pZHMgcG90ZW50aWFsIGJpYXNlcyB0aGF0IGNhbiBhcmlzZSBmcm9tIGltYmFsYW5jZWQgZGF0YXNldHMuCgotICAgY29sdW1uIG5hbWVzIGFuZCBmZWF0dXJlcyBjb250YWluIG5vIHNwYWNlcyBvciAiLSIuIGBSYCBkb2Vzbid0IHVzdWFsbHkgbGlrZSB0aGVzZS4KCiMjIyMgTGV0J3MgYnVpbGQgYSBwbG90ISAKCmBnZ3Bsb3QyYCBidWlsZHMgcGxvdHMgaW4gbGF5ZXJzLiBZb3UgY2FuIHN0YXJ0IHdpdGggYSBsYXllciBzaG93aW5nIHJhdyBkYXRhLCB0aGVuIGNvbnRpbnVlIHRvIGFkZCB1cCBhZGRpdGlvbmFsIGVsZW1lbnRzIHRvIHByb2R1Y2UgeW91ciBkZXNpcmVkIGdyYXBoLiBUaGlzIGFwcHJvYWNoIHdpbGwgaGVscCB5b3UgcmVkdWNlIHRoZSBnYXAgYmV0d2VlbiB0aGUgZXhwZWN0ZWQgb3V0Y29tZXMgaW4geW91ciBoZWFkIGFuZCB0aGUgcGxvdHMgaW4gcmVhbGl0eS4KCmBgYHtyfQpnZ3Bsb3QoZGF0YSA9IGlyaXMsICMgdXNlICdkYXRhJyBhcmd1bWVudCB0byB0ZWxsIGdncGxvdCB3aGljaCBkYXRhZnJhbWUgd2Ugd2FudCB0byBwbG90IGZyb20KICAgICAgIG1hcHBpbmcgPSBhZXMoeCA9IFBldGFsLkxlbmd0aCwgIyBtYXBwaW5nIGRldGVybWluZXMgd2hpY2ggdmFyaWFibGVzIGFyZSBhc3NpZ25lZCB0byBwbG90IGVsZW1lbnRzCiAgICAgICAgICAgICAgICAgICAgIHkgPSBQZXRhbC5XaWR0aCkpIC0+IGJhc2ljUGxvdAoKYmFzaWNQbG90CmBgYAoKCldlIGhhdmUgdG9sZCBgZ2dwbG90MmAgd2hpY2ggdmFyaWFibGVzIHdlIHdhbnQgdG8gcGxvdCBvbiB0aGUgeCBhbmQgeSBheGVzIGJ1dCB3ZSBoYXZlIG5vdCB0b2xkIGBnZ3Bsb3QyYCB3aGljaCBnZW9tZXRyaWMgZWxlbWVudHMgKGkuZS4gZ2VvbXMpIHRvIHVzZSB0byBjb25zdHJ1Y3QgdGhlIHBsb3QsIHNvIGFsbCB3ZSBoYXZlIGFyZSB0aGUgYXhlcyBhbmQgYSBibGFuayBwbG90LgoKIyMjIyBXaGF0IGFyZSBnZW9tcz8KCkdlb21zIGFyZSB0aGUgZ2VvbWV0cmljIG9iamVjdHMgKGUuZy4gbGluZXMsIGJhcnMsIGV0Yy4pIHRoYXQgZGV0ZXJtaW5lIGhvdyBvYnNlcnZhdGlvbnMgYXJlIHJlbmRlcmVkLiBMYXllcmluZyBlbGVtZW50cyBpbiBhIHBsb3QgdXN1YWxseSBzdGFydHMgd2l0aCBhZGRpbmcgZ2VvbXMuIExldCdzIGFkZCBgZ2VvbV9wb2ludCgpYCB0byBvdXIgYmFzaWMgcGxvdCB0byBjcmVhdGUgYSBzY2F0dGVyIHBsb3Q6CgpgYGB7cn0KZ2dwbG90KGRhdGEgPSBpcmlzLAogICAgICAgYWVzKHggPSBQZXRhbC5MZW5ndGgsICMgaXQgaXMgdmVyeSBjb21tb24gdG8gc2VlICdtYXBwaW5nID0nIG9taXR0ZWQgZnJvbSB0aGUgY29kZSAtIGdncGxvdCB3aWxsIGFjY2VwdCBlaXRoZXIKICAgICAgICAgICB5ID0gUGV0YWwuV2lkdGgpKSArICMgdXNlIGEgKyB0byBhZGQgZWxlbWVudHMgdG8geW91ciBwbG90CiAgZ2VvbV9wb2ludCgpIC0+IGJhc2ljU2NhdHRlcgoKYmFzaWNTY2F0dGVyCmBgYAoKCkFub3RoZXIgd2F5IHlvdSBjYW4gYWRkIGxheWVycyB0byBhIHBsb3QgaXMgYnkgc2ltcGx5IGFkZGluZyB0aGVtIHRvIHRoZSBlbmQgb2YgdGhlIG9iamVjdCB0aGF0IHdlIGFzc2lnbmVkIG91ciBmaXJzdCBwbG90IHRvLiBJdCBpcyB2ZXJ5IGNvbW1vbiB0byBzZWUgdGhpcyBpbiBvbmxpbmUgZ3VpZGVzIGFuZCBmb3J1bXMgKHN1Y2ggYXMgU3RhY2sgT3ZlcmZsb3cpIHdoZXJlIHlvdSBtaWdodCBsb29rIGZvciBoZWxwIHdpdGggYFJgIGNvZGluZzoKCmBgYHtyfQpiYXNpY1Bsb3QgKwogIGdlb21fcG9pbnQoKQpgYGAKCgpBbHRob3VnaCB0aGlzIGdlbmVyYXRlcyB0aGUgc2FtZSBvdXRwdXQsIEkgd291bGQgZ2VuZXJhbGx5IGF2b2lkIG1ha2luZyB5b3VyIHBsb3RzIHRoaXMgd2F5IC0gaWYgeW91IGVuZCB1cCB3aXRoIHNvbWV0aGluZyB0aGF0IGlzbid0IHF1aXRlIHdvcmtpbmcgYXMgZXhwZWN0ZWQgSSBmaW5kIGl0IGNhbiBiZSBlYXNpZXIgdG8gZml4IGlmIGFsbCB5b3VyIGNvZGUgaXMgbGFpZCBvdXQgaW4gZnJvbnQgb2YgeW91LCByYXRoZXIgdGhhbiBoYXZpbmcgdG8gcmV2aXNpdCBlYWNoIGluZGl2aWR1YWwgc3RlcCBpbiB0aGUgcHJvY2VzcyBvZiBtYWtpbmcgeW91ciBwbG90LgoKTm93IHdlJ2xsIHN0YXJ0IHRvIGFkZCBzb21lIG1vcmUgZWxlbWVudHMgdG8gb3VyIG1hcHBpbmcgYWVzdGhldGljcyB0byBiZXR0ZXIgaWxsdXN0cmF0ZSBvdXIgZGF0YS4KCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKKipJTVBPUlRBTlQgTk9URSoqCgpUaGUgaW5pdGlhbCBtYXBwaW5nIHRoYXQgeW91IHNwZWNpZnkgaW4gdGhlIGBnZ3Bsb3QyYCBjb21tYW5kIChpLmUuIGF4ZXMsIGNvbG9yLCBzaXplLCBldGMuKSBhcmUgYnkgZGVmYXVsdCB1c2VkIGdsb2JhbGx5IGZvciB0aGUgcGxvdCBhbmQgYXJlIGNhcnJpZWQgb3ZlciB0byBhbnkgZ2VvbXMgeW91IGFkZCBpbiB0aGUgZm9sbG93aW5nIGNvZGUuIEVhY2ggZ2VvbSAqY2FuKiBoYXZlIGl0J3Mgb3duIHNlcGFyYXRlIG1hcHBpbmcgYWVzdGhldGljcywgd2hpY2ggY2FuIGFsbG93IHlvdSB0byBjcmVhdGUgbW9yZSBjb21wbGV4IHBsb3RzLiBJZiB5b3UgZXZlciBydW4gaW50byBpc3N1ZXMgd2hlcmUgYSBnZW9tIGlzIG5vdCBiZWhhdmluZyBhcyB5b3Ugd291bGQgZXhwZWN0LCB0YWtlIGEgbG9vayBiYWNrIHRocm91Z2ggeW91ciBjb2RlIGFuZCBjaGVjayB3aGVyZSB5b3VyIGFlc3RoZXRpY3Mgd2VyZSBhc3NpZ25lZCwgYW5kIGhvdyB0aGV5IGFwcGx5IHRvIHRoZSBnZW9tcyB5b3UgYXJlIHRyeWluZyB0byBsYXllci4KCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKTGV0J3MgY29sb3Igb3VyIHBvaW50cyBieSBTcGVjaWVzOgoKYGBge3J9CmdncGxvdChkYXRhPWlyaXMsIAogICAgICAgbWFwcGluZyA9IGFlcyh4PVBldGFsLkxlbmd0aCwgCiAgICAgICAgICAgICAgICAgICAgIHk9UGV0YWwuV2lkdGgsIAogICAgICAgICAgICAgICAgICAgICBjb2xvciA9IFNwZWNpZXMpKSArICMgdGVsbCBnZ3Bsb3QgdGhhdCBjb2xvciBpcyBkZXRlcm1pbmVkIGJ5IFNwZWNpZXMgdmFyaWFibGUKICBnZW9tX3BvaW50KCkgLT4gY29sb3JTY2F0dGVyCiAgICAgICAgICAKY29sb3JTY2F0dGVyCmBgYAoKCk5vdyB3ZSBjYW4gYWRkIGEgc2ltcGxlIHJlZ3Jlc3Npb24gdXNpbmcgYGdlb21fc21vb3RoKClgIGFuZCB3ZSBjYW4gZGVtb25zdHJhdGUgaG93IGNoYW5naW5nIGdsb2JhbCB2cy4gc3BlY2lmaWMgYWVzdGhldGljcyBhZmZlY3QgZ2VvbXMuIFNvbWUgZ2VvbXMgaGF2ZSBzcGVjaWFsaXplZCBhcmd1bWVudHMgdGhhdCBhbGxvdyB0aGVtIHRvIGZ1bmN0aW9uLiBJbiB0aGlzIGNhc2UsIGBnZW9tX3Ntb290aCgpYCBhbGxvd3MgdXMgdG8gdGVsbCBpdCB3aGljaCBtZXRob2QgdG8gdXNlIHRvIGdlbmVyYXRlIHRoZSBjdXJ2ZSB0aGF0IGl0IHdpbGwgcGxvdC4gV2Ugd2lsbCBvcHQgZm9yIGBsbWAgd2hpY2ggaXMgYSBsaW5lYXIgbW9kZWwuCgpgYGB7cn0KZ2dwbG90KGRhdGE9aXJpcywgCiAgICAgICBtYXBwaW5nID0gYWVzKHg9UGV0YWwuTGVuZ3RoLCAKICAgICAgICAgICAgICAgICAgICAgeT1QZXRhbC5XaWR0aCwgCiAgICAgICAgICAgICAgICAgICAgIGNvbG9yID0gU3BlY2llcykpICsgCiAgZ2VvbV9wb2ludCgpICsKICBnZW9tX3Ntb290aChtZXRob2QgPSBsbSkgLT4gY29sb3JTY2F0dGVybG0KICAgICAgICAgIApjb2xvclNjYXR0ZXJsbQpgYGAKCgpUaGlzIGNyZWF0ZXMgdGhyZWUgc2VwYXJhdGUgY3VydmVzIHRoYXQgbWFwIHRvIHRoZSBwb2ludHMgZnJvbSBlYWNoIFNwZWNpZXMgYnkgY29sb3IsIGFzIHRoaXMgaXMgd2hhdCBpcyBzcGVjaWZpZWQgaW4gdGhlIGdsb2JhbCBhZXN0aGV0aWNzLiBMZXQncyBjaGFuZ2UgdGhhdCBhbmQgcGxvdCBhIGN1cnZlIHRoYXQgc3BhbnMgYWxsIHBvaW50czoKCmBgYHtyfQpnZ3Bsb3QoZGF0YT1pcmlzLCAKICAgICAgIG1hcHBpbmcgPSBhZXMoeD1QZXRhbC5MZW5ndGgsIAogICAgICAgICAgICAgICAgICAgICB5PVBldGFsLldpZHRoKSkgKyAjIHJlbW92ZSBjb2xvciBmcm9tIGdsb2JhbCBhZXN0aGV0aWNzCiAgZ2VvbV9wb2ludChhZXMoY29sb3IgPSBTcGVjaWVzKSkgKyAjIHNldCBnZW9tX3BvaW50IGFlc3RoZXRpY3MgLSB0aGlzIHdpbGwgb25seSBjb2xvciBwb2ludHMKICBnZW9tX3Ntb290aChtZXRob2QgPSBsbSkgLT4gY29sb3JTY2F0dGVybG0yCgpjb2xvclNjYXR0ZXJsbTIKYGBgCgoKTm93IHdlIGNhbiBzZWUgdGhhdCB0aGUgcG9pbnRzIGFyZSBzdGlsbCBjb2xvcmVkIGJ5IFNwZWNpZXMsIGJ1dCB0aGUgcmVncmVzc2lvbiBpcyBub3QuCgpMZXQncyBwbGF5IHdpdGggc29tZSBtb3JlIGFlc3RoZXRpY3M6CgpgYGB7cn0KZ2dwbG90KGRhdGE9aXJpcywgCiAgICAgICBtYXBwaW5nID0gYWVzKHg9UGV0YWwuTGVuZ3RoLCAKICAgICAgICAgICAgICAgICAgICAgeT1QZXRhbC5XaWR0aCkpICsgCiAgZ2VvbV9wb2ludChhZXMoY29sb3IgPSAiYmx1ZSIpKSArIAogIGdlb21fc21vb3RoKG1ldGhvZCA9IGxtKSAtPiBiYXNpY1Bsb3RCbHVlCgpiYXNpY1Bsb3RCbHVlCmBgYAoKCk5vdGljZSBob3cgZXZlbiB0aG91Z2ggd2UgaGF2ZSBjaGFuZ2VkIHRoZSBhZXN0aGV0aWMgb2YgdGhlIHBvaW50cyB0byBiZSAiYmx1ZSIsIGl0IGhhcyBub3QgbWFkZSB0aGVtIGJsdWUuIElmIHdlIHdhbnQgdG8gbWFrZSBhbGwgdGhlIHBvaW50cyBvbmUgY29sb3IgKG9yIGEgZGlmZmVyZW50IHNoYXBlLCBvciBhIGRpZmZlcmVudCBzaXplKSB0aGVzZSBhcmUgKm5vdCogc2V0IGJ5IGFlc3RoZXRpY3MsIGFzIHRoZXkgYXJlIG5vdCBkZXBlbmRlbnQgb24gYSB2YXJpYWJsZS4KCkxldCdzIG1ha2Ugb3VyIHBvaW50cyBibHVlIGFuZCBjaGFuZ2UgdGhlaXIgc2hhcGU6CgpgYGB7cn0KZ2dwbG90KGRhdGEgPSBpcmlzLAogICAgICAgbWFwcGluZyA9IGFlcyh4ID0gUGV0YWwuTGVuZ3RoLAogICAgICAgICAgICAgICAgICAgICB5ID0gUGV0YWwuV2lkdGgpKSArCiAgZ2VvbV9wb2ludChjb2xvciA9ICJibHVlIiwgIyBub3RlIHRoYXQgdGhlc2Ugb3B0aW9ucyBhcmUgbm90IHBhcnNlZCB0aHJvdWdoIHRoZSBhZXMoKSBhcmd1bWVudAogICAgICAgICAgICAgc2l6ZSA9IDUsCiAgICAgICAgICAgICBzaGFwZSA9IDEpIC0+IG9wZW5DaXJjbGVCbHVlCgpvcGVuQ2lyY2xlQmx1ZQoKIyBzaGFwZXMgYXJlIGRlZmluZWQgYnkgYSBudW1lcmljYWwgdmFsdWUKIyBhdmFpbGFibGUgc2hhcGVzIGNhbiBiZSB2aWV3ZWQgYXQgaHR0cHM6Ly93d3cuZGF0YW5vdmlhLmNvbS9lbi9ibG9nL2dncGxvdC1wb2ludC1zaGFwZXMtYmVzdC10aXBzLyBvciBieSB1c2luZyBnZ3B1YnI6OnNob3dfcG9pbnRfc2hhcGVzKCkKYGBgCgoKVGhlcmUgYXJlIG90aGVyIHdheXMgd2UgY2FuIGNvbnRyb2wgaG93IGVhY2ggbGF5ZXIgaXMgcmVuZGVyZWQuIExldCdzIHN0YXJ0IHdpdGggY29udHJvbGxpbmcgInNjYWxlcyIuIFNjYWxlcyBhbGxvdyB1cyB0byBlZGl0IHNwZWNpZmljIGVsZW1lbnRzIG9mIHRoZSBhZXN0aGV0aWNzIGFuZCBhcmUgbmFtZWQgaW4gYSB1bmlmb3JtIG1hbm5lciB0aGFuIGRlc2NyaWJlcyBob3cgdGhleSBhY3QgYW5kIHdoYXQgdGhleSBhZmZlY3QuIFRoZSBuYW1lcyBhcmUgbWFkZSB1cCBvZiB0aHJlZSBwaWVjZXMgc2VwYXJhdGVkIGJ5ICJfIjoKCi0gICBgc2NhbGVgCi0gICB0aGUgbmFtZSBvZiB0aGUgYWVzdGhldGljIChlLmcuIGBjb2xvcmAsIGBzaGFwZWAsIGBzaXplYCwgZXRjLikKLSAgIHRoZSBuYW1lIG9mIHRoZSBzY2FsZSAoZS5nLiBgbWFudWFsYCwgYGNvbnRpbnVvdXNgLCBgZGlzY3JldGVgLCBldGMuKQoKV2l0aGluIHRoZSBzY2FsZXMgdGhlcmUgYXJlIHNldmVyYWwgb3B0aW9ucyB3ZSBjYW4gZWRpdDogCgotIGBuYW1lID1gIHRoaXMgY29udHJvbHMgd2hhdCB0aGUgdmFyaWFibGUgaXMgY2FsbGVkIHdpdGhpbiB0aGUgcGxvdCBvciBsZWdlbmQgCi0gYHZhbHVlcyA9YCB0aGlzIGFsbG93cyB1cyB0byBtYW51YWxseSBpbnB1dCB0aGUgdmFyaWFibGVzIChpLmUuIGNvbG9ycyBvciBzaGFwZXMpIHVzZWQgaW4gdGhlIHBsb3QgCi0gYGxhYmVscyA9YCB0aGlzIGNvbnRyb2xzIHRoZSBkYXRhIGxhYmVscyAoaS5lLiBzcGVjaWVzIG5hbWVzKSBpbiB0aGUgcGxvdCBvciBsZWdlbmQKCkxldCdzIHVzZSBgc2NhbGVfY29sb3JfbWFudWFsYCB0byBtYW51YWxseSBzZWxlY3Qgc29tZSBjb2xvcnMgZm9yIG91ciBwbG90LgoKKFRoZXJlIGFyZSBzZXQgbmFtZWQgY29sb3JzIHRoYXQgY2FuIGJlIHVzZWQgaW4gYFJgIDxodHRwczovL3N0YXQuY29sdW1iaWEuZWR1L350emhlbmcvZmlsZXMvUmNvbG9yLnBkZj4gYnV0IHlvdSBjYW4gYWxzbyB1c2UgaGV4IGNvZGVzLiBNYWtlIHN1cmUgdG8gdXNlIGNvbG9yIGJsaW5kIGZyaWVuZGx5IGNvbG9yIHNjaGVtZXMgZm9yIGZpZ3VyZXMgeW91IHBsYW4gdG8gcHJlc2VudCBvciBwdWJsaXNoISkKCmBgYHtyfQpnZ3Bsb3QoZGF0YSA9IGlyaXMsCiAgICAgICBtYXBwaW5nID0gYWVzKHggPSBQZXRhbC5MZW5ndGgsCiAgICAgICAgICAgICAgICAgICAgIHkgPSBQZXRhbC5XaWR0aCwKICAgICAgICAgICAgICAgICAgICAgY29sb3IgPSBTcGVjaWVzKSkgKwogIGdlb21fcG9pbnQoKSArCiAgZ2VvbV9zbW9vdGgobWV0aG9kID0gbG0pICsKICBzY2FsZV9jb2xvcl9tYW51YWwobmFtZSA9ICJJcmlzIHNwZWNpZXMiLAogICAgICAgICAgICAgICAgICAgICB2YWx1ZXMgPSBjKCJzZXRvc2EiID0gInBpbmsiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ2ZXJzaWNvbG9yIiA9ICJwbHVtIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAidmlyZ2luaWNhIj0ic2VhZ3JlZW4zIiksCiAgICAgICAgICAgICAgICAgICAgIGxhYmVscyA9IGMoIklyaXMgc2V0b3NhIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiSXJpcyB2ZXJzaWNvbG9yIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiSXJpcyB2aXJnaW5pY2EiKSkgLT4gbXVsdGlDb2xvcgoKbXVsdGlDb2xvcgpgYGAKCgpOb3cgd2UgaGF2ZSBzb21lIGRpZmZlcmVudCBjb2xvcnMgYW5kIGRhdGEgbGFiZWxzIGluIG91ciBwbG90IG9mIHRoZSBJcmlzIGRhdGEuIFRoZSBgbmFtZWAgYW5kIGBsYWJlbHNgIG9wdGlvbnMgaW4gdGhlIGBzY2FsZWAgYXJlIHVzZWZ1bCBmb3IgY2hhbmdpbmcgaG93IHRoZSBkYXRhIGFyZSBsYWJlbGVkIGluIHlvdXIgcGxvdCAqd2l0aG91dCogbmVlZGluZyB0byBtYW5pcHVsYXRlIHRoZSByYXcgZGF0YS4KCldlIGNhbiBhbHNvIHVzZSBjb250aW51b3VzIGNvbG9yIHNjYWxlcyB0byB2aXN1YWxseSByZXByZXNlbnQgY2hhbmdlcyBpbiB2YWx1ZXM6CgpgYGB7cn0KZ2dwbG90KGRhdGEgPSBpcmlzLAogICAgICAgbWFwcGluZyA9IGFlcyh4ID0gUGV0YWwuTGVuZ3RoLAogICAgICAgICAgICAgICAgICAgICB5ID0gUGV0YWwuV2lkdGgpKSArCiAgZ2VvbV9wb2ludChhZXMoY29sb3IgPSBQZXRhbC5MZW5ndGgpKSArCiAgZ2VvbV9zbW9vdGgobWV0aG9kID0gbG0pIC0+IGJsdWVDb250CgpibHVlQ29udApgYGAKCgpZb3UgY2FuIGFsc28gdXNlIG90aGVyIHZhcmlhYmxlcyB3aXRoaW4gdGhlIGRhdGFmcmFtZSB0byBjb250cm9sIHRoZSBhZXN0aGV0aWNzIG9mIHRoZSBwbG90LgoKYGBge3J9CmdncGxvdChkYXRhID0gaXJpcywKICAgICAgIG1hcHBpbmcgPSBhZXMoeCA9IFBldGFsLkxlbmd0aCwKICAgICAgICAgICAgICAgICAgICAgeSA9IFBldGFsLldpZHRoKSkgKwogIGdlb21fcG9pbnQoYWVzKGNvbG9yID0gU2VwYWwuTGVuZ3RoLCAjIGNvbG9yIGlzIGRlcGVuZGVudCBvbiBzZXBhbCBsZW5ndGgKICAgICAgICAgICAgICAgICBzaXplID0gU2VwYWwuV2lkdGgpKSArICMgcG9pbnQgc2l6ZSBpcyBkZXBlbmRlbnQgb24gc2VwYWwgd2lkdGgKICBnZW9tX3Ntb290aChtZXRob2QgPSBsbSwKICAgICAgICAgICAgICBjb2xvciA9ICJibGFjayIsCiAgICAgICAgICAgICAgc2UgPSBGQUxTRSkgKyAjIGNoYW5nZSB0aGUgY29sb3Igb2YgdGhlIGN1cnZlCiAgc2NhbGVfY29sb3JfZ3JhZGllbnQoaGlnaCA9ICJwdXJwbGUiLAogICAgICAgICAgICAgICAgICAgICAgIGxvdyA9ICJvcmFuZ2UiKSAjIG1hbnVhbGx5IHNldCB0aGUgY29sb3JzIG9mIHRoZSBncmFkaWVudCBzY2FsZQpgYGAKCgpPYnZpb3VzbHkgdGhlcmUgaXMgYSBsaXR0bGUgdG9vIG11Y2ggZGF0YSBub3cgY29udGFpbmVkIGluIHRoaXMgcGxvdCBmb3IgaXQgdG8gYmUgcGFydGljdWxhcmx5IHVzZWZ1bCwgYnV0IGl0IGlzIGEgZ29vZCBleGFtcGxlIG9mIGhvdyBtdWNoIGRhdGEgeW91IGNhbiBkaXNwbGF5IGFuZCB0aGUgZGlmZmVyZW50IHdheXMgeW91IGNhbiBwcmVzZW50IGl0IHVzaW5nIGBSYC4KCkxldCdzIHRpZHkgdXAgb3VyIHBsb3QgYW5kIG1ha2Ugc29tZXRoaW5nIHRoYXQgbG9va3MgYSBsaXR0bGUgbW9yZSAicHVibGljYXRpb24tcmVhZHkiOgoKRmlyc3QsIGxldCdzIGFzc2lnbiBvdXIgY2hvc2VuIGNvbG9yIHNjYWxlIHRvIGEgdmVjdG9yIG9iamVjdCBzbyB3ZSBjYW4gY2FsbCB0aGUgc2FtZSBjb2xvcnMgZm9yIGFueSBmdXR1cmUgcGxvdHMgd2l0aG91dCBuZWVkaW5nIHRvIHdyaXRlIG91dCB0aGUgY29kZSBldmVyeSB0aW1lLiBUaGlzIHRpbWUgSSdtIGdvaW5nIHRvIHVzZSBhIGNvbG9yIGJsaW5kIGZyaWVuZGx5IHBhbGV0dGUgZ2VuZXJhdGVkIHVzaW5nIHRoaXMgdG9vbDogPGh0dHBzOi8vZGF2aWRtYXRobG9naWMuY29tL2NvbG9yYmxpbmQ+LgoKYGBge3J9CnBsb3RDb2xvcnMgPC0gYygic2V0b3NhIiA9ICIjNjQ4RkZGIiwKICAgICAgICAgICAgICAgICJ2ZXJzaWNvbG9yIiA9ICIjREMyNjdGIiwKICAgICAgICAgICAgICAgICJ2aXJnaW5pY2EiPSIjRkZCMDAwIikKYGBgCgpgYGB7cn0KZ2dwbG90KGRhdGEgPSBpcmlzLAogICAgICAgbWFwcGluZyA9IGFlcyh4ID0gUGV0YWwuTGVuZ3RoLAogICAgICAgICAgICAgICAgICAgICB5ID0gUGV0YWwuV2lkdGgpKSArCiAgZ2VvbV9wb2ludChhZXMoY29sb3IgPSBTcGVjaWVzKSkgKwogIGdlb21fc21vb3RoKG1ldGhvZCA9IGxtLAogICAgICAgICAgICAgIGNvbG9yID0gImJsYWNrIikgKwogIHRoZW1lX2J3KCkgKyAjIHRoaXMgaXMgYSBidWlsdC1pbiB0aGVtZSB0aGF0IHJlbW92ZXMgdGhlIGdyYXkgcGxvdCBiYWNrZ3JvdW5kCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IHBsb3RDb2xvcnMpICsgIyBkaXJlY3QgZ2dwbG90IHRvIG91ciBjb2xvciB2ZWN0b3IKICB5bGFiKCJQZXRhbCB3aWR0aCAobW0pIikgKyAjIGNoYW5nZSB5IGF4aXMgbGFiZWwgLSBjYW4gYWxzbyBiZSBkb25lIHdpdGggc2NhbGVzCiAgbGFicyh4ID0gIlBldGFsIGxlbmd0aCAobW0pIiwKICBjb2xvciA9ICJJcmlzIHNwZWNpZXMiKSArICMgY2hhbmdlIGxlZ2VuZCB0aXRsZSAtIGNhbiBhbHNvIGJlIGRvbmUgd2l0aCBzY2FsZXMgYXMgcHJldmlvdXNseQogIGdndGl0bGUoIlBldGFsIHdpZHRoIGJ5IHBldGFsIGxlbmd0aCBwZXIgc3BlY2llcyIpICsgIyBhZGQgcGxvdCB0aXRsZSAKICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSkgLT4gbXVsdGlDb2xvclRpZHkgIyBjZW50ZXIgcGxvdCB0aXRsZQoKbXVsdGlDb2xvclRpZHkKYGBgCgoKUGVyZmVjdCEgTm93IHdlIGNhbiB3cml0ZSBvdXIgcGxvdCB0byBhIHBkZjoKCmBgYHtyfQpwZGYoIm11bHRpLmNvbG9yLmlyaXMucGxvdC5sbS5wZGYiLCAjIGZpbGUgbmFtZSB0byB3cml0ZSB0bwogICAgaGVpZ2h0ID0gNCwgIyBwbG90IGhlaWdodCBpbiBpbmNoZXMKICAgIHdpZHRoID0gNikgIyBwbG90IHdpZHRoIGluIGluY2hlcwoKbXVsdGlDb2xvclRpZHlMbSAjIHRlbGwgUiB3aGljaCBwbG90IHRvIHdyaXRlIHRvIGZpbGUKCmRldi5vZmYoKSAjIHRoaXMgdGVsbHMgUiB0aGF0IHlvdSdyZSBkb25lIGNyZWF0aW5nIGEgZmlsZQpgYGAKCgpPciB3ZSBjYW4gdXNlIGBnZ3NhdmUoKWAsIHdoaWNoIGlzIGEgZnVuY3Rpb24gb2YgYGdncGxvdDJgIHRvIHNhdmUgYXMgYW55IG90aGVyIGdyYXBoaWNzIGZpbGUgdHlwZToKCmBgYHtyfQpnZ3NhdmUocGxvdCA9IG11bHRpQ29sb3JUaWR5TG0sICMgc3BlY2lmeSBwbG90CiAgICAgICAibXVsdGkuY29sb3IuaXJpcy5wbG90LmxtLnRpZmYiLCAjIHNwZWNpZnkgZmlsZSBuYW1lCiAgICAgICBoZWlnaHQgPSA0LCAjIHBsb3QgaGVpZ2h0CiAgICAgICB3aWR0aCA9IDYsICMgcGxvdCB3aWR0aAogICAgICAgdW5pdHMgPSBjKCJpbiIpLCAjIHNwZWNpZnkgd2hpY2ggdW5pdHMgdG8gdXNlIGZvciBoZWlnaHQgYW5kIHdpZHRoCiAgICAgICBkZXZpY2UgPSAidGlmZiIpICMgc3BlY2lmeSBmaWxlIHR5cGUgZm9yIHNhdmluZyAtIGdnc2F2ZSB3aWxsIGFsc28gZ3Vlc3MgZGVwZW5kaW5nIG9uIHRoZSBleHRlbnNpb24gdXNlZCBpbiBmaWxlIG5hbWUgCmBgYAoKCiMjIyMgUGxvdHRpbmcgZ3JvdXAgbWVhbnMgYW5kIGVycm9yIGJhcnMKCkFub3RoZXIgd2F5IHRoYXQgd2UgbWF5IHdhbnQgdG8gcGxvdCBvdXIgZGF0YSBpcyBieSBwbG90dGluZyBib3RoIGdyb3VwIG1lYW5zICphbmQqIGluZGl2aWR1YWwgZGF0YSBwb2ludHMuIFRoaXMgY2FuIGhlbHAgcGVvcGxlIGJldHRlciB2aXN1YWxpemUgdGhlIHNwcmVhZCBvZiBvdXIgZGF0YS4gVGhpcyBpcyBlYXN5IGVub3VnaCB3aXRoIGdlb21zIGxpa2UgYGdlb21fYm94cGxvdCgpYCBhbmQgYGdlb21fdmlvbGluKClgIHRoYXQgaGF2ZSBncm91cCBtZXRyaWNzIGJ1aWx0IGludG8gdGhlaXIgZnVuY3Rpb25hbGl0eS4KCmBgYHtyfQpnZ3Bsb3QoZGF0YSA9IGlyaXMsCiAgICAgICBhZXMoeCA9IFNwZWNpZXMsCiAgICAgICAgICAgeSA9IFBldGFsLkxlbmd0aCwKICAgICAgICAgICBmaWxsID0gU3BlY2llcykpICsgCiAgZ2VvbV9ib3hwbG90KG91dGxpZXJzID0gRkFMU0UpICsKICBnZW9tX3BvaW50KCkKYGBgCgoKWW91IGNhbiBzZWUgdGhhdCBgZ2VvbV9ib3hwbG90YCBoYXMgYXV0b21hdGljYWxseSBnZW5lcmF0ZWQgYSBib3ggdGhhdCBkaXNwbGF5cyB0aGUgbWVkaWFuICh0aGljayBsaW5lKSBhbmQgYm94IHRoYXQgc3BhbnMgdGhlIDI1dGggLSA3NXRoIHBlcmNlbnRpbGVzLCB3aXRoIHdoaXNrZXJzIHRoYXQgZXh0ZW5kIHRvIHRoZSBmdXJ0aGVzdCB2YWx1ZSBubyBtb3JlIHRoYW4gMS41IFggdGhlIElRUiBmcm9tIHRoZSBib3guIFZhbHVlcyBiZXlvbmQgdGhlIHdoaXNrZXJzIHdvdWxkIGJlIGNvdW50ZWQgYXMgb3V0bGllcnMgYW5kIHBsb3R0ZWQgc2VwYXJhdGVseS4gVGhpcyBpcyBncmVhdCBmb3IgdGFraW5nIGEgcXVpY2sgc3VtbWFyeSBsb29rIGF0IHlvdXIgZGF0YS4gCgpCdXQgd2hhdCBpZiB3ZSB3YW50IHRvIHVzZSBzb21ldGhpbmcgbGlrZSBgZ2VvbV9iYXIoKWAgdGhhdCBkb2VzIG5vdCBoYXZlIGJ1aWx0IGluIGdyb3VwIGZ1bmN0aW9uYWxpdHk/CgpUaGVyZSBhcmUgYSBjb3VwbGUgb2Ygd2F5cyB3ZSBjb3VsZCBzb2x2ZSB0aGlzIGlzc3VlIHVzaW5nIGZ1bmN0aW9ucyBpbiB0aGUgYHRpZHl2ZXJzZWAgcGFja2FnZS4gRmlyc3QsIHdlIGNvdWxkIGNyZWF0ZSBhIHN1bW1hcnkgdGFibGUgdGhhdCBjb250YWlucyBncm91cGVkIGluZm9ybWF0aW9uIGZvciBvdXIgZGF0YSB1c2luZyBgc3VtbWFyaXNlYC4KCmBgYHtyfQppcmlzICU+JQogIGdyb3VwX2J5KFNwZWNpZXMpICU+JSAjIGdyb3VwX2J5IHRlbGxzIFIgd2hpY2ggdmFyaWFibGUgdG8gdXNlIHRvIGdyb3VwIG9ic2VydmF0aW9ucwogIHN1bW1hcmlzZShtZWFuLlBldGFsLkxlbmd0aCA9IG1lYW4oUGV0YWwuTGVuZ3RoKSwgIyBhZGQgYSBjb2x1bW4gY29udGFpbmluZyBtZWFuIHZhbHVlcyBwZXIgc3BlY2llcwogICAgICAgICAgICBzdGFuZGFyZC5kZXZpYXRpb24gPSBzZChQZXRhbC5MZW5ndGgpKSAtPiBpcmlzU3VtbWFyeSAjIGFkZCBhIGNvbHVtbiBjb250YWluaW5nIHN0YW5kYXJkIGRldmlhdGlvbgoKaGVhZChpcmlzU3VtbWFyeSkKYGBgCgoKV2UgY2FuIHVzZSB0aGUgYHN1bW1hcmlzZWAgZnVuY3Rpb24gdG8gY3JlYXRlIGEgbmV3IGRhdGFmcmFtZSB0aGF0IGNvbnRhaW5zIGEgbWVhbiBhbmQgc3RhbmRhcmQgZGV2aWF0aW9uIGZvciBlYWNoIHNwZWNpZXMuIFdlIGNhbiB3cml0ZSB0aGlzIHRvIGEgbmV3IG9iamVjdCBhbmQgdGhlbiB1c2UgdGhpcyBmb3IgcGxvdHRpbmcgYnkgcHJvdmlkaW5nIGVhY2ggYGdlb21gIHdpdGggYSBkaWZmZXJlbnQgZGF0YWZyYW1lLgoKYGBge3J9CmdncGxvdCgpICsgIyB3ZSBkbyBub3Qgd2FudCBnbG9iYWwgbWFwcGluZyBvciBkYXRhIGZvciB0aGlzIHBsb3Qgc28gbm9uZSBpcyBwdXQgaW4gdGhlIGdncGxvdCBjYWxsCiAgZ2VvbV9jb2woZGF0YSA9IGlyaXNTdW1tYXJ5LCAjIHNldCB0aGUgZGF0YWZyYW1lIGZvciB0aGUgY29sdW1ucwogICAgICAgICAgIGFlcyh4ID0gU3BlY2llcywKICAgICAgICAgICAgICAgeSA9IG1lYW4uUGV0YWwuTGVuZ3RoLAogICAgICAgICAgICAgICBmaWxsID0gU3BlY2llcyksCiAgICAgICAgICAgYWxwaGEgPSAwLjUpICsKICBnZW9tX2Vycm9yYmFyKGRhdGEgPSBpcmlzU3VtbWFyeSwgIyBzZXQgdGhlIGRhdGFmcmFtZSBmb3IgdGhlIGVycm9yIGJhcnMKICAgICAgICAgICAgICAgIGFlcyh4ID0gU3BlY2llcywKICAgICAgICAgICAgICAgICAgICB5bWluID0gKG1lYW4uUGV0YWwuTGVuZ3RoIC0gc3RhbmRhcmQuZGV2aWF0aW9uKSwgIyBzZXQgdGhlIG1pbmltdW0gZXJyb3IgYmFyIHZhbHVlCiAgICAgICAgICAgICAgICAgICAgeW1heCA9IChtZWFuLlBldGFsLkxlbmd0aCArIHN0YW5kYXJkLmRldmlhdGlvbikpLCAjIHNldCB0aGUgbWF4aW11bSBlcnJvciBiYXIgdmFsdWUKICAgICAgICAgICAgICAgIHdpZHRoID0gMC4yKSArCiAgZ2VvbV9qaXR0ZXIoZGF0YSA9IGlyaXMsICMgc2V0IHRoZSBkYXRhZnJhbWUgZm9yIHRoZSBwb2ludHMKICAgICAgICAgICAgICBhZXMoeCA9IFNwZWNpZXMsCiAgICAgICAgICAgICAgICAgIHkgPSBQZXRhbC5MZW5ndGgsCiAgICAgICAgICAgICAgICAgIGNvbG9yID0gU3BlY2llcyksCiAgICAgICAgICAgICAgd2lkdGggPSAwLjIsICMgbWFrZSB0aGUgdG90YWwgc3ByZWFkIG9mIHRoZSBwb2ludHMgbmFycm93ZXIKICAgICAgICAgICAgICBzaGFwZSA9IDEpICMgc2V0IHRoZSBzaGFwZSB0byBvcGVuIGNpcmNsZQpgYGAKCgpOb3cgd2UgY2FuIHNlZSBib3RoIHRoZSBtZWFuIGFuZCBpbmRpdmlkdWFsIHZhbHVlcyBvbiBvdXIgYmFyIHBsb3QuCgpBbm90aGVyLCBtb3JlIHN0cmVhbWxpbmVkLCB3YXkgb2YgZG9pbmcgdGhpcyBpcyB1c2luZyBgc3RhdF9zdW1tYXJ5YCwgd2hlcmUgd2UgcmVtb3ZlIHRoZSBuZWVkIHRvIGNyZWF0ZSBhIHNlcGFyYXRlIGRhdGFmcmFtZSBieSB1c2luZyBmdW5jdGlvbnMgd2l0aGluIHRoZSBgZ2dwbG90YCBwYWNrYWdlLiAKCmBgYHtyfQpnZ3Bsb3QoZGF0YSA9IGlyaXMsCiAgICAgICBhZXMoeCA9IFNwZWNpZXMsCiAgICAgICAgICAgeSA9IFBldGFsLkxlbmd0aCkpICsKICBzdGF0X3N1bW1hcnkoZ2VvbSA9ICJjb2wiLCAjIGlkZW50aWZ5IHdoaWNoIGdlb20gd2Ugd2FudAogICAgICAgICAgICAgICBmdW4uZGF0YSA9IG1lYW5fc2UsICMgdGVsbCBzdGF0X3N1bW1hcnkgd2hpY2ggZnVuY3Rpb24gdG8gYXBwbHkgdG8gc3VtbWFyaXNlIHRoZSBkYXRhCiAgICAgICAgICAgICAgIGFlcyhmaWxsID0gU3BlY2llcyksICMgc2V0IGFlc3RoZXRpY3MgYXMgbm9ybWFsCiAgICAgICAgICAgICAgIGFscGhhID0gMC41KSArCiAgc3RhdF9zdW1tYXJ5KGdlb20gPSAiZXJyb3JiYXIiLAogICAgICAgICAgICAgICBmdW4uZGF0YSA9IG1lYW5fc2UsCiAgICAgICAgICAgICAgIGNvbG9yID0gImJsYWNrIiwKICAgICAgICAgICAgICAgd2lkdGggPSAwLjIpICsKICBnZW9tX2ppdHRlcihhZXMoY29sb3IgPSBTcGVjaWVzKSwKICAgICAgICAgICAgICBzaGFwZSA9IDEsCiAgICAgICAgICAgICAgd2lkdGggPSAwLjIpCmBgYAoKClZvaWzDoCEgV2UgaGF2ZSAqYWxtb3N0KiBzYW1lIHBsb3QgYXMgYWJvdmUgYnV0IHdpdGggYSBzdGVwIHJlbW92ZWQuIEhvd2V2ZXIsIHlvdSBtYXkgaGF2ZSBub3RpY2VkIHRoYXQgd2UgdXNlZCBmdW5jdGlvbiBgbWVhbl9zZWAsIHdoaWNoIGNhbGN1bGF0ZXMgdGhlIG1lYW4gYW5kIHN0YW5kYXJkIGVycm9yIGZvciBhIHZlY3RvciBvZiBgeWAgdmFsdWVzIGF0IGVhY2ggdW5pcXVlIGB4YCB2YWx1ZSAoKmkuZS4qIHRoZSBmdW5jdGlvbiByZWNlaXZlcyBhIHZlY3RvciBvZiB2YWx1ZXMgZm9yIFBldGFsLkxlbmd0aCBmb3IgZWFjaCBTcGVjaWVzKSBhbmQgbW9zdCBvZiB0aGUgdGltZSB3ZSBsaWtlIHRvIHVzZSBzdGFuZGFyZCBkZXZpYXRpb24uIGBzdGF0X3N1bW1hcnlgIGRvZXMgbm90IG9mZmVyIHRoaXMgZnVuY3Rpb24gYXMgcGFydCBvZiB0aGUgcGFja2FnZSAtIHNvIHdoYXQgZG8gd2UgZG8/IENyZWF0ZSBvdXIgb3duLgoKYGBge3J9Cm1lYW4uc2QgPC0gZnVuY3Rpb24oeCl7CiAgdGliYmxlKHkgPSBtZWFuKHgpLCAjIHRlbGwgdGhlIGZ1bmN0aW9uIHRoYXQgd2Ugd2FudCBhIHRpYmJsZSBvdXRwdXQgKHNpbWlsYXIgdG8gZGF0YWZyYW1lKQogICAgICAgICB5bWluID0geSAtIHNkKHgpLCAjIGNhbGN1bGF0ZXMgdGhlIG1pbmltdW0gdmFsdWUgZm9yIGVycm9yIGJhcgogICAgICAgICB5bWF4ID0geSArIHNkKHgpKSAjIGNhbGN1bGF0ZXMgdGhlIG1heGltdW0gdmFsdWUgZm9yIGVycm9yIGJhcgp9CmBgYAoKCk5vdyB3ZSBjYW4gY3JlYXRlIG91ciBwbG90OgoKYGBge3J9CmdncGxvdChkYXRhID0gaXJpcywKICAgICAgIGFlcyh4ID0gU3BlY2llcywKICAgICAgICAgICB5ID0gUGV0YWwuTGVuZ3RoKSkgKwogIHN0YXRfc3VtbWFyeShnZW9tID0gImNvbCIsIAogICAgICAgICAgICAgICBmdW4uZGF0YSA9IG1lYW4uc2QsIAogICAgICAgICAgICAgICBhZXMoZmlsbCA9IFNwZWNpZXMpLAogICAgICAgICAgICAgICBhbHBoYSA9IDAuNSkgKwogIHN0YXRfc3VtbWFyeShnZW9tID0gImVycm9yYmFyIiwKICAgICAgICAgICAgICAgZnVuLmRhdGEgPSBtZWFuLnNkLAogICAgICAgICAgICAgICBjb2xvciA9ICJibGFjayIsCiAgICAgICAgICAgICAgIHdpZHRoID0gMC4yKSArCiAgZ2VvbV9qaXR0ZXIoYWVzKGNvbG9yID0gU3BlY2llcyksCiAgICAgICAgICAgICAgc2hhcGUgPSAxLAogICAgICAgICAgICAgIHdpZHRoID0gMC4yKQpgYGAKCiMjIyMgRmFjZXRzCgpGYWNldGluZyBpcyBhIHRlY2huaXF1ZSB0aGF0IGFsbG93cyB1cyB0byBzZXBhcmF0ZSBkYXRhIG91dCBpbnRvIHBhbmVscyBiYXNlZCBvbiBhIHZhcmlhYmxlIGluIHRoZSBkYXRhZnJhbWUuIFRoaXMgaXMgdXNlZnVsIGZvciB2aXN1YWxpemluZyBjb21wbGV4IGRhdGEgd2hlcmUgaXQgbWF5IGJlIGVhc2llciB0byBzZWUgcGF0dGVybnMgd2hlbiB0aGUgZGF0YSBhcmUgc2VwYXJhdGVkLgoKVGhlcmUgYXJlIHR3byBtZXRob2RzIHRvIGNyZWF0ZSBmYWNldHMgaW4gYSBwbG90OiBgZmFjZXRfd3JhcCgpYCBhbmQgYGZhY2V0X2dyaWQoKWAuIElmIHlvdSBhcmUgb25seSBjcmVhdGluZyBmYWNldHMgYmFzZWQgb24gb25lIHZhcmlhYmxlIChlLmcuIHNwZWNpZXMpIHlvdSBjYW4gdXNlIGBmYWNldF93cmFwKClgIGJ1dCBpZiB5b3UgaGF2ZSBhIG1vcmUgY29tcGxleCBwbG90IHdoZXJlIHlvdSB3YW50IHRvIGNyZWF0ZSBmYWNldHMgYmFzZWQgb24gdHdvIHZhcmlhYmxlcyAoZS5nLiBzcGVjaWVzICphbmQqIHRpbWUgcG9pbnQpIHlvdSBuZWVkIHRvIHVzZSBgZmFjZXRfZ3JpZCgpYC4KCkxldCdzIHB1bGwgdXAgYW5vdGhlciBvZiBgUmAncyBidWlsdC1pbiBkYXRhc2V0cyAobXRjYXJzKSB0aGF0IHdpbGwgYWxsb3cgdXMgdG8gc2VlIGJvdGggb2YgdGhlc2UgaW4gYWN0aW9uLiBtdGNhcnMgaXMgYnVpbHQgZnJvbSBkYXRhIGV4dHJhY3RlZCBmcm9tIHRoZSAxOTc0IE1vdG9yIFRyZW5kIFVTIG1hZ2F6aW5lLCBhbmQgY29tcHJpc2VzIGZ1ZWwgY29uc3VtcHRpb24gYW5kIDEwIGFzcGVjdHMgb2YgYXV0b21vYmlsZSBkZXNpZ24gYW5kIHBlcmZvcm1hbmNlIGZvciAzMiBhdXRvbW9iaWxlcyAoMTk3My0tNzQgbW9kZWxzKS4KCmBgYHtyfQpoZWFkKG10Y2FycykKYGBgCgoKTGV0J3MgbG9vayBhdCBtcGcgKE1pbGVzIHBlciBVUyBHYWxsb24pIHBsb3R0ZWQgYWdhaW5zdCBocCAoR3Jvc3MgaG9yc2Vwb3dlcikuCgpgYGB7cn0KZ2dwbG90KGRhdGEgPSBtdGNhcnMsCiAgICAgICBhZXMoeCA9IGhwLAogICAgICAgICAgIHkgPSBtcGcsCiAgICAgICAgICAgY29sb3IgPSBtcGcpKSArCiAgZ2VvbV9wb2ludChzaXplID0gMykKYGBgCgoKTm93IGxldCdzIHVzZSBgZmFjZXRfd3JhcCgpYCB0byBzcGxpdCB0aGVzZSBkYXRhIHVwIGJ5IHZzIChFbmdpbmdlIHNoYXBlLCAwID0gViwgMSA9IHN0cmFpZ2h0KS4KCmBgYHtyfQpnZ3Bsb3QoZGF0YSA9IG10Y2FycywKICAgICAgIGFlcyh4ID0gaHAsCiAgICAgICAgICAgeSA9IG1wZywKICAgICAgICAgICBjb2xvciA9IG1wZykpICsKICBnZW9tX3BvaW50KHNpemUgPSAzKSArCiAgZmFjZXRfd3JhcCh+IHZzKQpgYGAKCgpMZXQncyBhZGQgYW5vdGhlciB2YXJpYWJsZSBmYWNldCB3aXRoIGBmYWNldF9ncmlkKClgIGFuZCBzcGxpdCB0aGUgZGF0YSBieSBhbSAoVHJhbnNtaXNzaW9uLCAwID0gYXV0b21hdGljLCAxID0gbWFudWFsKSBhcyB3ZWxsLgoKYGBge3J9CmdncGxvdChkYXRhID0gbXRjYXJzLAogICAgICAgYWVzKHggPSBocCwKICAgICAgICAgICB5ID0gbXBnLAogICAgICAgICAgIGNvbG9yID0gbXBnKSkgKwogIGdlb21fcG9pbnQoc2l6ZSA9IDMpICsKICBmYWNldF9ncmlkKGNvbHMgPSB2YXJzKHZzKSwgIyBhc3NpZ24gYSB2YXJpYWJsZSB0byB0aGUgY29sdW1uIHBhbmVscwogICAgICAgICAgICAgcm93cyA9IHZhcnMoYW0pKSAjIGFzc2lnbiBhIHZhcmlhYmxlIHRvIHRoZSByb3cgcGFuZWxzCmBgYAoKCldlIGNhbiBzZWUgdGhhdCB0aGVyZSBhcmUgZGlmZmVyZW50IGNvcnJlbGF0aW9ucyBiZXR3ZWVuIGhwIGFuZCBtcGcgZGVwZW5kaW5nIG9uIHRoZSBvdGhlciBxdWFsaXRpZXMgb2YgdGhlIGNhci4gSG93ZXZlciwgdGhpcyBwbG90IGlzIG5vdyBkaWZmaWN1bHQgdG8gcmVhZCBiZWNhdXNlIGJvdGggdmFyaWFibGVzIGFyZSBiaW5hcmllcywgbWVhbmluZyBpdCdzIGhhcmQgdG8gdGVsbCB3aGF0J3Mgd2hhdC4gTGV0J3MgdGlkeSB1cCB0aGVzZSBwbG90cyBhbmQgYWRkIHNvbWUgbGFiZWxzLgoKQ2hhbmdpbmcgdGhlIHBhbmVsIGxhYmVscyB3aXRob3V0IGNoYW5naW5nIHRoZSB1bmRlcmx5aW5nIGRhdGEgaXMgc2xpZ2h0bHkgbW9yZSBjb21wbGV4IHRoYW4gY2hhbmdpbmcgYXhpcyB0aXRsZXMsIHNvIGxldCdzIGxvb2sgYXQgaG93IHRvIGRvIHRoYXQuCgpgYGB7cn0KdnNMYWJzIDwtIGMoIjAiID0gIlYtc2hhcGVkIiwKICAgICAgICAgICAgIjEiID0gIlN0cmFpZ2h0IikgIyBjcmVhdGUgYSB2ZWN0b3IgdGhhdCBtYXRjaGVzIHRoZSBiaW5hcnkgdmFyaWFibGVzIHRvIHRoZWlyIHZhbHVlcwoKYW1MYWJzIDwtIGMoIjAiID0gIkF1dG9tYXRpYyIsCiAgICAgICAgICAgICIxIiA9ICJNYW51YWwiKSAjIGRvIHRoZSBzYW1lIGZvciB0aGUgYW0gdmFyaWFibGUKCmdncGxvdChkYXRhID0gbXRjYXJzLAogICAgICAgYWVzKHggPSBocCwKICAgICAgICAgICB5ID0gbXBnLAogICAgICAgICAgIGNvbG9yID0gbXBnKSkgKwogIGdlb21fcG9pbnQoc2l6ZSA9IDMpICsKICBmYWNldF9ncmlkKGNvbHMgPSB2YXJzKHZzKSwKICAgICAgICAgICAgIHJvd3MgPSB2YXJzKGFtKSwKICAgICAgICAgICAgIGxhYmVsbGVyID0gbGFiZWxsZXIoLmNvbHMgPSB2c0xhYnMsICMgdXNlIHRoZSBsYWJlbGxlciBmdW5jdGlvbiB0byBhc3NpZ24gdGhlc2UgbGFiZWxzIHRvIHRoZSByb3dzIGFuZCBjb2x1bW5zIG9mIHRoZSBwbG90CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC5yb3dzID0gYW1MYWJzKSkgLT4gZmFjZXRQbG90CgpmYWNldFBsb3QKYGBgCgoKTGV0J3MgdGlkeSB0aGUgcmVzdCBvZiB0aGlzIHBsb3QgdXAgYW5kIHRoZW4gc2F2ZSBpdCB0byBmaWxlLgoKYGBge3J9CmZhY2V0UGxvdCArCiAgc2NhbGVfY29sb3JfZ3JhZGllbnQobmFtZSA9ICJNaWxlcyBwZXJcblVTIEdhbGxvbiIsICMgXG4gc3RhcnRzIGEgbmV3IGxpbmUgaW4gdGhlIGxlZ2VuZCB0aXRsZQogICAgICAgICAgICAgICAgICAgICAgIGhpZ2ggPSAicHVycGxlIiwKICAgICAgICAgICAgICAgICAgICAgICBsb3cgPSAib3JhbmdlIikgKyAjIGNoYW5nZSBjb2xvciBvZiBzY2FsZQogIHRoZW1lX2J3KCkgKwogIHhsYWIoIkdyb3NzIGhvcnNlcG93ZXIiKSArICMgYWRkIHggYXhpcyB0aXRsZQogIHlsYWIoIk1pbGVzIHBlciBVUyBHYWxsb24iKSArICMgYWRkIHkgYXhpcyB0aXRsZQogIHRoZW1lKHN0cmlwLmJhY2tncm91bmQgPSBlbGVtZW50X3JlY3QoZmlsbCA9ICJ3aGl0ZSIpKSAtPiBmYWNldFBsb3RUaWR5ICMgcmVtb3ZlIGdyZXkgYmFja2dyb3VuZCBmcm9tIHBhbmVsIHRpdGxlcwoKZmFjZXRQbG90VGlkeQpgYGAKCgpOb3cgd2UgY2FuIHVzZSB0aGUgc2FtZSBtZXRob2RzIGFzIGVhcmxpZXIgdG8gc2F2ZSBvdXIgcGxvdCB0byBlaXRoZXIgYSBQREYgb3IgaW1hZ2UgZmlsZSAob3IgYm90aCEpLgoKYGBge3J9CnBkZigibXVsdGkuZmFjZXQubXRjYXJzLnBsb3QucGRmIiwgIyBmaWxlIG5hbWUgdG8gd3JpdGUgdG8KICAgIGhlaWdodCA9IDQsICMgcGxvdCBoZWlnaHQgaW4gaW5jaGVzCiAgICB3aWR0aCA9IDYpICMgcGxvdCB3aWR0aCBpbiBpbmNoZXMKCmZhY2V0UGxvdFRpZHkgIyB0ZWxsIFIgd2hpY2ggcGxvdCB0byB3cml0ZSB0byBmaWxlCgpkZXYub2ZmKCkgIyB0aGlzIHRlbGxzIFIgdGhhdCB5b3UncmUgZG9uZSBjcmVhdGluZyBhIGZpbGUKYGBgCgpgYGB7cn0KZ2dzYXZlKHBsb3QgPSBmYWNldFBsb3RUaWR5LCAjIHNwZWNpZnkgcGxvdAogICAgICAgIm11bHRpLmZhY2V0Lm10Y2Fycy5wbG90LnRpZmYiLCAjIHNwZWNpZnkgZmlsZSBuYW1lCiAgICAgICBoZWlnaHQgPSA0LCAjIHBsb3QgaGVpZ2h0CiAgICAgICB3aWR0aCA9IDYsICMgcGxvdCB3aWR0aAogICAgICAgdW5pdHMgPSBjKCJpbiIpLCAjIHNwZWNpZnkgd2hpY2ggdW5pdHMgdG8gdXNlIGZvciBoZWlnaHQgYW5kIHdpZHRoCiAgICAgICBkZXZpY2UgPSAidGlmZiIpICMgc3BlY2lmeSBmaWxlIHR5cGUgZm9yIHNhdmluZyAtIGdnc2F2ZSB3aWxsIGFsc28gZ3Vlc3MgZGVwZW5kaW5nIG9uIHRoZSBleHRlbnNpb24gdXNlZCBpbiBmaWxlIG5hbWUKYGBgCgoKIyMjIyBQbG90dGluZyBhIHRpbWUgY291cnNlCgpGb3IgbWFueSBleHBlcmltZW50cywgaXQncyBpbXBvcnRhbnQgdG8gYmUgYWJsZSB0byBwbG90IGEgdGltZSBjb3Vyc2UuIExldCdzIGxvYWQgaW4gc29tZSBleGFtcGxlIGNvbG9ueSBjb3VudCBkYXRhIGZyb20gYW4gZXhwZXJpbWVudCBncm93aW5nIGZvdXIgc3BlY2llcyBvZiBiYWN0ZXJpYSBpbiBib3RoIGhpZ2ggYW5kIGxvdyBpcm9uIGNvbmRpdGlvbnMsIHdpdGggdGltZSBwb2ludHMgYXQgMCwgNiwgYW5kIDI0IGhvdXJzLgoKYGBge3J9CnJlYWQuY3N2KCJjZnVfY291bnRzX3Jhdy5jc3YiKSAtPiBjb3VudHMgIyByZWFkIGluIGNvdW50cwpgYGAKCgpMZXQncyB0YWtlIGEgcXVpY2sgbG9vayBhdCB0aGUgZm9ybWF0IG9mIHRoZSBkYXRhIHdlIGp1c3QgbG9hZGVkIGFuZCBjaGVjayB0aGF0IHRoZSBmb3JtYXQgbG9va3MgY29ycmVjdCBmb3IgcGxvdHRpbmcuCgpgYGB7cn0KaGVhZChjb3VudHMpCmBgYAoKCk91ciBkYXRhZnJhbWUgaGFzIGNvbHVtbnMgYXMgKip2YXJpYWJsZXMqKiBhbmQgcm93cyBhcyAqKm9ic2VydmF0aW9ucyoqIHNvIHdlJ3JlIGdvb2QgdG8gZ28hIAoKSW4gb3JkZXIgdG8gcGxvdCBhIHRpbWUgY291cnNlIGFzIGEgZGlzY3JldGUgdmFyaWFibGUgdGhhdCBydW5zIGFsb25nIHRoZSB4IGF4aXMsIHdlIG5lZWQgdG8gY2hhbmdlIHRoZSBgdGltZWAgdmFyaWFibGUgZnJvbSBudW1lcmljIHRvIGEgZmFjdG9yIGluIGJvdGggdGhlIHJhdyBjb3VudHMgYW5kIGdyb3VwIG1lYW5zIGRhdGFmcmFtZXMuIEZhY3RvcnMgY2FuIGhlbHAgdXMgY29udHJvbCB0aGUgb3JkZXIgaW4gd2hpY2ggb2JzZXJ2YXRpb25zIGFyZSBwbG90dGVkLiBCeSBkZWZhdWx0LCBnZ3Bsb3Qgd2lsbCBwbG90IG51bWVyaWMgdmFyaWFibGVzIGluIGFzY2VuZGluZyBvcmRlciBhbmQgY2hhcmFjdGVyIG9yIGZhY3RvciB2YXJpYWJsZXMgaW4gYWxwaGFiZXRpY2FsIG9yZGVyLiBTbywgd2UnbGwgYWxzbyBzZXQgdGhlIGlyb24gbGV2ZWwgYXMgYSBmYWN0b3IgYmVjYXVzZSBJIHdhbnQgdG8gcGxvdCB0aGUgbG93IGlyb24gY29uZGl0aW9uICpiZWZvcmUqIHRoZSBoaWdoIGlyb24gY29uZGl0aW9uLgoKYGBge3J9CmNvdW50cyR0aW1lIDwtIGZhY3Rvcihjb3VudHMkdGltZSkKCmNvdW50cyRpcm9uIDwtIGZhY3Rvcihjb3VudHMkaXJvbiwKICAgICAgICAgICAgICAgICAgICAgIGxldmVscyA9IGMoIkxvdyBpcm9uIiwiSGlnaCBpcm9uIikpCmBgYAoKCk5vdyB3ZSBjYW4gc2V0IG91ciBjdXN0b20gY29sb3JzIGZvciB0aGUgcGxvdC4KCmBgYHtyfQpzcGVjaWVzQ29scyA8LSBjKCJQc2V1ZG9tb25hcyBhZXJ1Z2lub3NhIiA9ICIjNDNiYThmIiwKICAgICAgICAgICAgIlN0YXBoeWxvY29jY3VzIGF1cmV1cyIgPSAiI2ZlYzQ0ZiIsCiAgICAgICAgICAgICJTdHJlcHRvY29jY3VzIHNhbmd1aW5pcyIgPSAiIzQyOTJjNiIsCiAgICAgICAgICAgICJCdXJraG9sZGVyaWEgb3JiaWNvbGEiID0gIiNkNTdiZDQiKQpgYGAKCgpMZXQncyBjcmVhdGUgYSBsaW5lIHBsb3Qgb2YgbG9nMTAgQ0ZVL21MIHBlciBzcGVjaWVzIG92ZXIgdGltZSwgd2l0aCBmYWNldHMgc2hvd2luZyB0aGUgaGlnaCBhbmQgbG93IGlyb24uIFdlIHdpbGwgcGxvdCBhIHJpYmJvbiB0aGF0IHJlcHJlc2VudHMgdGhlIHN0YW5kYXJkIGRldmlhdGlvbiAoYHNkYCksIHRoaW4gbGluZXMgdGhhdCByZXByZXNlbnQgZWFjaCByZXBsaWNhdGUgKGB0ZWNoLnJlcGApLCBhbmQgYSB0aGljayBsaW5lIHJlcHJlc2VudGluZyB0aGUgbWVhbiBDRlUvbUwgZm9yIGVhY2ggc3BlY2llcyAoYG1lYW4uY2Z1YCkuIFdlJ2xsIHV0aWxpemUgdGhlIGBzdGF0X3N1bW1hcnlgIGZ1bmN0aW9uIHRoYXQgd2Ugc2F3IGVhcmxpZXIuCgpgYGB7cn0KY291bnRzICU+JQogIG11dGF0ZShsb2cxMC5jZnUgPSBsb2cxMChjZnUpKSAlPiUgIyBjcmVhdGUgYSBuZXcgY29sdW1uIHdpdGggbG9nMTAgQ0ZVIHZhbHVlcwogIGdncGxvdChhZXMoeCA9IHRpbWUsCiAgICAgICAgICAgICB5ID0gbG9nMTAuY2Z1KSkgKwogIHN0YXRfc3VtbWFyeShnZW9tID0gInJpYmJvbiIsCiAgICAgICAgICAgICAgIGZ1bi5kYXRhID0gbWVhbi5zZCwKICAgICAgICAgICAgICAgYWVzKGdyb3VwID0gc3BlY2llcywgIyBncm91cCBhZXN0aGV0aWMgc3BlY2lmaWVzIGhvdyB0aGUgbGluZXMgYXJlIGpvaW5lZCB0b2dldGhlcgogICAgICAgICAgICAgICAgICAgZmlsbCA9IHNwZWNpZXMpLAogICAgICAgICAgICAgICBhbHBoYSA9IDAuNSkgKwogIGdlb21fbGluZShhZXMoZ3JvdXAgPSBpbnRlcmFjdGlvbihzcGVjaWVzLGlyb24sdGVjaC5yZXApLAogICAgICAgICAgICAgICAgY29sb3IgPSBzcGVjaWVzKSwKICAgICAgICAgICAgbGluZXdpZHRoID0gMC4xLAogICAgICAgICAgICBhbHBoYSA9IDAuNykgKwogIHN0YXRfc3VtbWFyeShnZW9tID0gImxpbmUiLAogICAgICAgICAgICAgICBmdW4uZGF0YSA9IG1lYW4uc2QsCiAgICAgICAgICAgICAgIGFlcyhjb2xvciA9IHNwZWNpZXMsCiAgICAgICAgICAgICAgICAgICBncm91cCA9IGludGVyYWN0aW9uKHNwZWNpZXMsaXJvbikpLAogICAgICAgICAgICAgICBsaW5ld2lkdGggPSAxKSArCiAgc2NhbGVfY29sb3JfbWFudWFsKG5hbWUgPSAiU3BlY2llcyIsICMgYm90aCBjb2xvciBhbmQgZmlsbCBtdXN0IGhhdmUgdGhlIHNhbWUgbmFtZSBpZiB3ZSB3YW50IHRvIGNvbWJpbmUgdGhlIGxlZ2VuZAogICAgICAgICAgICAgICAgICAgICB2YWx1ZXMgPSBzcGVjaWVzQ29scykgKwogIHNjYWxlX2ZpbGxfbWFudWFsKG5hbWUgPSAiU3BlY2llcyIsCiAgICAgICAgICAgICAgICAgICAgdmFsdWVzID0gc3BlY2llc0NvbHMpICsKICBsYWJzKHggPSAiVGltZSAoaCkiLCAjIHNldCBheGlzIGxhYmVscwogICAgICAgeSA9ICJMb2c8c3ViPjEwPC9zdWI+IENGVS9tTCIpICsKICB0aGVtZV9idygpICsgIyByZW1vdmUgZ3JleSBiYWNrZ3JvdW5kCiAgZmFjZXRfZ3JpZChjb2xzID0gdmFycyhpcm9uKSkgKyAjIGZhY2V0IHBsb3QgYnkgaGlnaC9sb3cgaXJvbgogIHRoZW1lKHN0cmlwLmJhY2tncm91bmQgPSBlbGVtZW50X3JlY3QoZmlsbCA9ICJ3aGl0ZSIsICMgcmVtb3ZlIGdyZXkgYmFja2dyb3VuZCBmcm9tIGZhY2V0IHRpdGxlcwogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sb3IgPSAiYmxhY2siKSwKICAgICAgICBsZWdlbmQudGV4dCA9IGVsZW1lbnRfdGV4dChmYWNlID0gIml0YWxpYyIpLCAjIHNldCBsZWdlbmQgZm9udCB0byBpdGFsaWMKICAgICAgICBsZWdlbmQucG9zaXRpb24gPSAiaW5zaWRlIiwgIyBtb3ZlIGxlZ2VuZCBpbnNpZGUgYm91bmRzIG9mIHBsb3QKICAgICAgICBsZWdlbmQucG9zaXRpb24uaW5zaWRlID0gYygwLjgsMC4yKSwgIyB1c2UgYSB2ZWN0b3IgdG8gc2V0IHggYW5kIHkgcG9zaXRpb24gKDAgLSAxKQogICAgICAgIGxlZ2VuZC5iYWNrZ3JvdW5kID0gZWxlbWVudF9yZWN0KGZpbGwgPSAid2hpdGUiLCAjIHNldCBib3ggYXJvdW5kIGxlZ2VuZAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbG9yID0gImJsYWNrIiksCiAgICAgICAgLAogICAgICAgIGF4aXMudGl0bGUueSA9IGVsZW1lbnRfbWFya2Rvd24oKSkgIyBhbGxvdyBheGlzIHRpdGxlIHRvIHJlYWQgaHRtbCBjb2RlIGZvciBzdWJzY3JpcHQKYGBgCgoKIyMjIEFjdGl2aXRpZXMKCiMjIyMgR3JlZW4gMQoKQ3JlYXRlIGEgc2NhdHRlciBwbG90IHVzaW5nIHRoZSBjb2x1bW5zIFNlcGFsLkxlbmd0aCAoeCkgYW5kIFNlcGFsLldpZHRoICh5KSBmcm9tIHRoZSBpcmlzIGRhdGFzZXQuCgpgYGB7cn0KZ2dwbG90KGRhdGEgPSBpcmlzLCAKICAgICAgIGFlcyh4ID0gU2VwYWwuTGVuZ3RoLCAKICAgICAgICAgICB5ID0gU2VwYWwuV2lkdGgpKSArIAogIGdlb21fcG9pbnQoYWVzKGNvbG9yID0gU3BlY2llcykpICsKICBnZW9tX3Ntb290aChtZXRob2QgPSBsbSkKYGBgCgoKIyMjIyBHcmVlbiAyCgpNYWtlIGEgcGxvdCB3aGVyZSBhbGwgdGhlIHBvaW50cyBhcmUgZ3JlZW4sIGFuZCB0aGUgbGluZSBpcyBjb2xvcmVkIGJ5IHRoZSBzcGVjaWVzIG9mIGlyaXMuCgpgYGB7cn0KZ2dwbG90KGRhdGEgPSBpcmlzLAogICAgICAgYWVzKHggPSBQZXRhbC5MZW5ndGgsCiAgICAgICAgICAgeSA9IFBldGFsLldpZHRoKSkgKwogIGdlb21fcG9pbnQoY29sb3IgPSAiZ3JlZW4iKSArCiAgZ2VvbV9zbW9vdGgoYWVzKGNvbG9yID0gU3BlY2llcyksCiAgICAgICAgICAgICAgbWV0aG9kID0gbG0pCmBgYAoKCiMjIyMgQmx1ZSAxCgpNYWtlIGEgcGxvdCB0aGF0IGluY2x1ZGVzIHJlZ3Jlc3Npb24gbGluZXMgZm9yIGluZGl2aWR1YWwgc3BlY2llcyBhcyB3ZWxsIGFzIHRoZSBvdmVyYWxsIGRhdGEuCgpgYGB7cn0KZ2dwbG90KGRhdGEgPSBpcmlzLCAKICAgICAgIGFlcyh4ID0gUGV0YWwuTGVuZ3RoLCAKICAgICAgICAgICB5ID0gUGV0YWwuV2lkdGgpKSArCiAgZ2VvbV9wb2ludChhZXMoY29sb3IgPSBTcGVjaWVzKSkgKwogIGdlb21fc21vb3RoKGFlcyhjb2xvciA9IFNwZWNpZXMpLCAKICAgICAgICAgICAgICBtZXRob2QgPSBsbSkgKwogIGdlb21fc21vb3RoKG1ldGhvZCA9IGxtLCAKICAgICAgICAgICAgICBjb2xvcj0iYmx1ZSIpCmBgYA==